using System.Collections; using UnityEngine; using Mapbox.Unity.Map; using Mapbox.Utils; using Assets.Scripts.Apis.Models; using static Assets.Scripts.Apis.Models.MapDataModel; using System; using Random = UnityEngine.Random; using Assets.Scripts.Apis; using GeoJSON.Net.Geometry; using TurfCS; using System.Linq; using System.Collections.Generic; using System.Text; using System.Globalization; using Assets.Scenes.Ride.Scripts.Model; using static CyclingController; using System.IO; namespace Assets.Scenes.Ride.Scripts { public class PlayerController : MonoBehaviour { [Header("Character")] [SerializeField] GameObject character; [SerializeField] Animator characterAnimator; //[SerializeField] AbstractMap map; #region 动画控制参数 Vector3 nextPos; Vector3 prePos; Vector2d currentlatlong; //当前坐标 Vector2d nextlatlong; //下一个点的坐标 float timer = 1.0f;//计时器 #endregion #region 选手骑行数据 bool isStart; bool isMajor;//是否是主人公 MapDataModel mapData; int userId; int routeId; DateTime startTime;//开始骑行时间 double speed; double power; double gradev; double elevation; double cadance; int heartRate; int ticks; double totalDistance; double currentSlope; double nextSlope; double nextSlopeDistance; double distance; public int UserId { get => userId; } public bool IsStart { get => isStart; } public double Speed { get => speed; } public double Power { get => power; } public double Cadance { get => cadance; } public double HeartRate { get => heartRate; } public int TotalTicks { get => ticks; } public double TotalDistance { get => totalDistance; } public double CurrentSlope { get => currentSlope; } public double NextSlope { get => nextSlope;} public double NextSlopeDistance { get => nextSlopeDistance; } public double Gradev { get => gradev; } public double Elevation { get => elevation; } #endregion void Start() { characterAnimator = GetComponentInChildren(); var mainController = transform.parent.GetComponent(); map = FindObjectOfType(); mapData = mainController.GetMapData();//获取路书信息 routeId = mainController.RouteId;//路数id Initialize(mainController.GetCenterCoordinate());//初始人物 } void Update() { timer -= Time.deltaTime; if (timer <= 0)//定时器 一秒执行一次 { Run(); timer = 1.0f; } } #region 游戏状态控制 //开始骑行 public void SetStart() { isStart = true; startTime = DateTime.Now; } //继续骑行 public void SetContinue() { isStart = true; } //暂停骑行 public void SetPause() { isStart = false; Complete(); } #endregion #region 骑行逻辑 //初始化玩家距离/朝向 void Initialize(double[] coordinates) { ticks = 0;//当前骑行时间 //初始化人物位置 totalDistance = 0;//TODO:根据骑行模式动态取值 KM currentlatlong = Along(totalDistance);//new Vector2d(coordinates[0], coordinates[1]);//当前坐标 nextlatlong = currentlatlong;//下一秒坐标 ////初始化人物转向 //var secondPlace = mapData.List[10].Point; //var secondVect3d = map.GeoToWorldPosition( new Vector2d(secondPlace[0], secondPlace[1])); //Quaternion firstRotation = Quaternion.LookRotation(transform.localPosition - secondVect3d); //character.transform.rotation = Quaternion.Euler(0, firstRotation.eulerAngles.y, 0); } public List RiderDatas = new List(); //骑行中 void Run() { if (IsStart) { //CamControl(); ComputeMapData();//计算海拔&坡度&下一个点信息 power = 2000;//功率 speed = Helper.CalculateSpeed(elevation, 0, power, 65, 7); distance = Math.Round(speed / 3600, 6); totalDistance += distance; characterAnimator.SetBool("IsRide", false);//初始化动画状态 if (totalDistance <= mapData.TotalDistance) { //数据处理 ticks++; nextlatlong = Along(totalDistance);//下一个坐标 nextPos = map.GeoToWorldPosition(nextlatlong);//下一个点 nextPos.y += 0.3f; prePos = transform.localPosition;//当前点 //记录骑行数据 var recordText = string.Format($"{ ticks },{ power.ToString(CultureInfo.InvariantCulture) },{ speed.ToString(CultureInfo.InvariantCulture) },{ Math.Round(totalDistance, 6).ToString(CultureInfo.InvariantCulture) },{ cadance.ToString(CultureInfo.InvariantCulture) },{ heartRate.ToString(CultureInfo.InvariantCulture) },{ Math.Round(nextlatlong.x, 6).ToString(CultureInfo.InvariantCulture) },{ Math.Round(nextlatlong.y, 6).ToString(CultureInfo.InvariantCulture) }"); Debug.Log(recordText); RiderDatas.Add(new TargetData { Ticks = ticks, _Power = power, _Speed = speed, _Distance = totalDistance, _Cadence = cadance, _HeartRate = heartRate, _Lat = nextlatlong.x, _Lon = nextlatlong.y }); //动画控制 if (distance > 0) { characterAnimator.SetBool("IsRide", true);//开始移动动画 StartCoroutine(LookAtNextPos());//转向 StartCoroutine(MoveTo());//移动 } } else { totalDistance = mapData.TotalDistance; //记录骑行数据 var recordText = string.Format($"{ ticks },{ power.ToString(CultureInfo.InvariantCulture) },{ speed.ToString(CultureInfo.InvariantCulture) },{ Math.Round(totalDistance, 6).ToString(CultureInfo.InvariantCulture) },{ cadance.ToString(CultureInfo.InvariantCulture) },{ heartRate.ToString(CultureInfo.InvariantCulture) },{ Math.Round(nextlatlong.x, 6).ToString(CultureInfo.InvariantCulture) },{ Math.Round(nextlatlong.y, 6).ToString(CultureInfo.InvariantCulture) }"); Debug.Log(recordText); RiderDatas.Add(new TargetData { Ticks = ticks, _Power = power, _Speed = speed, _Distance = totalDistance, _Cadence = cadance, _HeartRate = heartRate, _Lat = nextlatlong.x, _Lon = nextlatlong.y }); Complete(); isStart = false; characterAnimator.SetBool("ReachEnd", true); } } else { characterAnimator.SetBool("IsRide", false); } } //发送当前用户位置给UDPs void SendUdp() { } //TODO:骑行结束 private int weight = 65; private int bicycleWeight = 7; private string ContinueMark = "TODO"; private int ContinueIndex=0; private int Competitionid =0; private bool IsNeedRanking = true; private double CurrentRouteStartDistance = 0; private int ManufacturerId=0; private int AntModelId =0 ; private CyclingModel cyclingModel = CyclingModel.Single; private DateTime EndTime; void Complete() { EndTime = startTime.AddSeconds(ticks); MapInterruptRecordApi api = new MapInterruptRecordApi(); string newFileName = Guid.NewGuid().ToString(); int FTP = Helper.GetFtp(); double NP = Helper.GetNP(RiderDatas); //强度 double IF = NP / FTP; //训练量 double TSS = (RiderDatas.Count * NP * IF) / (FTP * 3600) * 100; var recordId = Guid.NewGuid().ToString(); var averagePower = Helper.AveragePower(RiderDatas); var interruptRecord = new MapInterruptRecord { Id = recordId, RouteId = routeId, RouteName = "", TotalDistance = mapData.TotalDistance, UserId = userId, RecordFileName = newFileName + ".txt", Ftp = FTP,//FTP设置 IF = Math.Round(IF, 2), Kj = RiderDatas.Sum(a => a._Power) / 1000,//消耗 Tss = Math.Round(TSS, 2), EndDistance = totalDistance, IsCompleted = totalDistance >= mapData.TotalDistance, NormalizedPower = Math.Floor(NP),//标准化功率 AveragePower = averagePower,//平均功率 MaxPower = RiderDatas.Max(a => a._Power),//最大功率 WeightKg = Math.Round(averagePower / weight, 2), Weight = weight, BicycleWeight = bicycleWeight, ContinueMark = ContinueMark, ContinueIndex = ContinueIndex, IsDelete = false, MapCompetitionId = Competitionid, ManufacturerName = "TODO", DeviceNumber = "TODO", IsRanking = IsNeedRanking, CurrentRouteStartDistance = CurrentRouteStartDistance, ManufacturerId = ManufacturerId, AntModelId = AntModelId, StartTime = startTime, CreateTime = EndTime, Ticks = RiderDatas.Last().Ticks, Mode = cyclingModel.ToString(), //Param = Newtonsoft.Json.JsonConvert.SerializeObject(selectParam), //GlobalCyclingId = selectParam.GlobalCyclingId }; interruptRecord.SpeedRange = null; double process = Math.Round((totalDistance - interruptRecord.CurrentRouteStartDistance) /mapData.TotalDistance, 2); interruptRecord.Progress = process > 1 ? 1 : process; var cadences = RiderDatas.Where(a => a._Cadence.HasValue && a._Cadence.Value > 0); if (cadences.Any()) { interruptRecord.AverageCadence = Math.Round(cadences.Average(a => a._Cadence.GetValueOrDefault(0))); } interruptRecord.MaxCadence = Math.Round(RiderDatas.Max(a => a._Cadence.GetValueOrDefault(0))); interruptRecord.AverageHeartRate = Math.Round(RiderDatas.Average(a => a._HeartRate.GetValueOrDefault(0))); interruptRecord.MaxHeartRate = RiderDatas.Max(a => a._HeartRate.GetValueOrDefault(0)); var path = Helper.GetDataDire("MapWorkoutRecords"); var fname = path + "/" + newFileName + ".txt"; var files = new List(); using (var fs = new FileInfo(fname).OpenWrite()) { var stream = new StreamWriter(fs); stream.BaseStream.Seek(0, SeekOrigin.End); foreach (var item in RiderDatas) { stream.Write(item.Write() + "\r\n"); } stream.Flush(); stream.Close(); files.Add(fname); } var result = api.Add(interruptRecord, files); } #endregion #region 工具类 public int CurrentIndex; //当前距离所在的海拔/坡度/距离 下一个点的坡度以及剩余距离 void ComputeMapData() { double sumDistance = 0; var pointList = mapData.List; int index = 0; for (int i = 0; i < pointList.Count; i++) { sumDistance += pointList[i].Distance; if (totalDistance <= sumDistance) { index = i; break; } } //计算当前海拔和坡度 elevation = pointList[index].Elevation; gradev = pointList[index].Grade; //计算下一个点的坡度和距离 int nextIndex = index == pointList.Count - 1 ? index : index + 1; CurrentIndex = nextIndex; nextSlope = pointList[nextIndex].Grade; nextSlopeDistance = sumDistance + pointList[nextIndex].Distance - totalDistance; } //根据距离计算坐标 Vector2d Along(double endDistance) { if (mapData != null) { var list = mapData.List.Select(p => new GeoJSON.Net.Geometry.GeographicPosition(p.Point[0], p.Point[1])); LineString lineString = new LineString(list); var pt1 = Turf.Along(lineString, endDistance); var ll = ((GeographicPosition)((GeoJSON.Net.Geometry.Point)pt1.Geometry).Coordinates); return new Vector2d(ll.Latitude, ll.Longitude); } return nextlatlong; } #endregion #region 人物移动与转向控制 IEnumerator LookAtNextPos() { Quaternion neededRotation = Quaternion.LookRotation(transform.localPosition - nextPos); Quaternion thisRotation = character.transform.localRotation; float t = 0; while (t < 1.0f) { t += Time.deltaTime / 0.25f; var rotationValue = Quaternion.Slerp(thisRotation, neededRotation, t); character.transform.rotation = Quaternion.Euler(0, rotationValue.eulerAngles.y, 0); yield return null; } } //人物移动控制 IEnumerator MoveTo() { //让人物移动分点增加动画的流畅度 float t = 0; while (t < 1) { t += Time.deltaTime; Vector3 v = Vector3.Lerp(prePos, nextPos, t); transform.localPosition = v; //控制海拔图的位置 yield return null; } } #endregion #region 相机控制 //[Header("CameraSettings")] //[SerializeField] //Camera cam; //Vector3 previousPos = Vector3.zero; //Vector3 deltaPos = Vector3.zero; // IEnumerator CamControl()//替换成cinemachine //{ // if (cam != null) // { // float t = 0; // while (t < 1.0f) // { // t += Time.deltaTime / 0.5f; // deltaPos = transform.position - previousPos; // //deltaPos.y = 0; // cam.transform.position = Vector3.Lerp(cam.transform.position, cam.transform.position + deltaPos, t); // previousPos = transform.position; // yield return null; // } // } //} #endregion } }