289 lines
8.0 KiB
C#
289 lines
8.0 KiB
C#
using Assets.Scenes.Ride.Scripts.Model.CyclingModels;
|
||
using Assets.Scripts.Apis.Models;
|
||
using GeoJSON.Net.Geometry;
|
||
using Mapbox.Unity.Map;
|
||
using Mapbox.Utils;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using TurfCS;
|
||
using UnityEngine;
|
||
|
||
namespace Assets.Scenes.Ride.Scripts
|
||
{
|
||
public abstract class AbstractPlayer: MonoBehaviour
|
||
{
|
||
[Header("Character")]
|
||
[SerializeField]
|
||
GameObject character;
|
||
[SerializeField]
|
||
Animator characterAnimator;
|
||
|
||
AbstractMap map;
|
||
//AbstractMap map;
|
||
|
||
#region 动画控制参数
|
||
Vector3 nextPos;
|
||
Vector3 prePos = Vector3.zero;
|
||
float timer = 1.0f;//计时器
|
||
#endregion
|
||
|
||
#region 选手骑行数据
|
||
protected Vector2d nextlatlong; //下一个点的坐标
|
||
protected Vector2d currentlatlong; //当前坐标
|
||
//protected bool isStart;//开始或者暂停
|
||
//public bool isQuit;//true 中途退出 或者到达终点
|
||
protected bool isMajor;//是否是主人公
|
||
protected MapDataModel mapData;
|
||
protected int userId;
|
||
protected DateTime startTime;//开始骑行时间
|
||
protected DateTime endTime;//结束骑行时间
|
||
protected double weight;//体重
|
||
protected double bicycleWeight;//车重
|
||
protected double speed;
|
||
protected double power;
|
||
protected double elevation;
|
||
protected double cadance;
|
||
protected int heartRate;
|
||
protected int ticks;
|
||
protected double totalDistance;
|
||
protected double currentSlope;
|
||
protected double nextSlope;
|
||
protected double nextSlopeDistance;
|
||
protected double distance;
|
||
protected double currentSlopeDistance;
|
||
protected double lastEndDistance;
|
||
protected double totalClimb;
|
||
|
||
public double TotalClimb { get => totalClimb; }
|
||
public int UserId { get => userId; }
|
||
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 Distance { get => distance; }
|
||
public double CurrentSlope { get => currentSlope; }
|
||
public double NextSlope { get => nextSlope; }
|
||
public double NextSlopeDistance { get => nextSlopeDistance; }
|
||
public double CurrentSlopeDistance { get => currentSlopeDistance; }
|
||
public double Elevation { get => elevation; }
|
||
public double LastEndDistance { get => lastEndDistance; }
|
||
public Vector2d Currentlatlong { get => currentlatlong; }
|
||
#endregion
|
||
|
||
|
||
void Start()
|
||
{
|
||
Init();
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
Excute();
|
||
}
|
||
|
||
#region 骑行逻辑
|
||
//初始化骑行数据
|
||
protected CyclingController mainController;
|
||
protected BaseCycling cyclingExcutor;
|
||
protected virtual void Init()
|
||
{
|
||
characterAnimator = GetComponentInChildren<Animator>();
|
||
mainController = transform.parent.GetComponent<CyclingController>();
|
||
map = transform.parent.Find("Map").GetComponent<AbstractMap>();
|
||
mapData = mainController.GetMapData();//获取路书信息
|
||
}
|
||
|
||
protected virtual void Excute()
|
||
{
|
||
//CamControl();
|
||
timer -= Time.deltaTime;
|
||
if (timer <= 0)//定时器 一秒执行一次
|
||
{
|
||
SendTcp();
|
||
Run();
|
||
timer = 1.0f;
|
||
}
|
||
}
|
||
//骑行中
|
||
protected virtual void Run()
|
||
{
|
||
ComputeNextSlope();//计算下一个坡度相关数据
|
||
//人物动画控制
|
||
if (characterAnimator != null && mapData != null)
|
||
{
|
||
characterAnimator.SetFloat("Speed", (float)speed);
|
||
if (totalDistance >= mapData.TotalDistance)
|
||
{
|
||
characterAnimator.SetBool("ReachEnd", true);//到达终点
|
||
}
|
||
}
|
||
//开始骑行
|
||
if (mainController.isStart)
|
||
{
|
||
if (index > 0)
|
||
{
|
||
totalClimb = 0;
|
||
//计算累计爬升
|
||
for (int i = 1; i <= index; i++)
|
||
{
|
||
var diff = mapData.List[i].Elevation - mapData.List[i - 1].Elevation;
|
||
if (diff > 0)
|
||
{
|
||
totalClimb += diff;
|
||
}
|
||
}
|
||
}
|
||
ticks++;
|
||
Compute();//接受蓝牙设备数据计算
|
||
|
||
if (totalDistance > mapData.TotalDistance)
|
||
{
|
||
distance = totalDistance - mapData.TotalDistance;
|
||
totalDistance = mapData.TotalDistance;
|
||
StartCoroutine("LateUpload");
|
||
}
|
||
|
||
//数据处理
|
||
currentlatlong = Along(totalDistance);//下一个坐标
|
||
nextPos = map.GeoToWorldPosition(currentlatlong);//下一个点
|
||
nextPos.y += 0.6f;//提高y轴让人物站在地图上面
|
||
prePos = transform.localPosition;//当前点
|
||
thisRotation = transform.localRotation;
|
||
//移动动画控制
|
||
if (distance > 0)
|
||
{
|
||
StartCoroutine(MoveTo());//移动
|
||
}
|
||
}
|
||
}
|
||
protected virtual void SendTcp()
|
||
{
|
||
|
||
}
|
||
//计算功率 速度 当前骑行总里程(M)心率 踏频 等
|
||
protected virtual void Compute()
|
||
{
|
||
//power = 900;//功率
|
||
//speed = Helper.CalculateSpeed(elevation, 0, power, 65, 7);
|
||
//distance = Math.Round(speed / 3600, 6);
|
||
//totalDistance += distance;
|
||
}
|
||
//当前用户调用来上传骑行记录
|
||
public virtual void Upload()
|
||
{
|
||
}
|
||
#endregion
|
||
|
||
#region 工具类
|
||
|
||
IEnumerator LateUpload()
|
||
{
|
||
yield return new WaitForSeconds(1);
|
||
Upload();
|
||
StopCoroutine("LateUpload");
|
||
}
|
||
|
||
public int CurrentIndex;
|
||
private int index;
|
||
public double CurrentDistance;//当前所处区间距离
|
||
//当前距离所在的海拔/坡度/距离 下一个点的坡度以及剩余距离
|
||
void ComputeNextSlope()
|
||
{
|
||
double sumDistance = 0;
|
||
if (mapData == null)
|
||
return;
|
||
var pointList = mapData.List;
|
||
int preIndex = 0;
|
||
for (int i = 0; i < pointList.Count; i++)
|
||
{
|
||
sumDistance += pointList[i].Distance;
|
||
if (totalDistance * 1000 <= sumDistance)
|
||
{
|
||
index = i;
|
||
break;
|
||
}
|
||
}
|
||
preIndex = index > 0 ? index - 1:0;
|
||
//计算当前海拔和坡度
|
||
elevation = pointList[index].Elevation;
|
||
currentSlope = pointList[index].Grade;
|
||
//计算下一个点的坡度和距离
|
||
int nextIndex = index == pointList.Count - 1 ? index : index + 1;
|
||
CurrentIndex = nextIndex;
|
||
nextSlope = pointList[nextIndex].Grade;
|
||
nextSlopeDistance = sumDistance - totalDistance * 1000;
|
||
currentSlopeDistance = totalDistance * 1000 - (sumDistance - pointList[index].Distance);
|
||
//计算当前区间距离
|
||
CurrentDistance = pointList[CurrentIndex].Grade;
|
||
}
|
||
//根据距离计算坐标
|
||
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 currentlatlong;
|
||
}
|
||
#endregion
|
||
|
||
#region 人物移动与转向控制
|
||
|
||
Quaternion thisRotation;
|
||
IEnumerator LookAtNextPos()
|
||
{
|
||
Quaternion neededRotation = Quaternion.LookRotation(prePos - nextPos);
|
||
//Quaternion thisRotation = character.transform.localRotation;
|
||
|
||
float t = 0;
|
||
while (t < 1.0f)
|
||
{
|
||
t += Time.deltaTime / 0.5f;
|
||
var rotationValue = Quaternion.Slerp(thisRotation, neededRotation, t);
|
||
character.transform.rotation = Quaternion.Euler(0, rotationValue.eulerAngles.y, 0);
|
||
yield return null;
|
||
}
|
||
}
|
||
Vector3 deltaPos = Vector3.zero;
|
||
Vector3 previousPos = Vector3.zero;
|
||
void CamControl()
|
||
{
|
||
deltaPos = transform.position - previousPos;
|
||
deltaPos.y = 0;
|
||
Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, Camera.main.transform.position + deltaPos, Time.time);
|
||
previousPos = transform.position;
|
||
}
|
||
//人物移动控制
|
||
IEnumerator MoveTo()
|
||
{
|
||
StartCoroutine(LookAtNextPos());//转向
|
||
//让人物移动分点增加动画的流畅度
|
||
float t = 0;
|
||
while (t < 1)
|
||
{
|
||
t += Time.deltaTime;
|
||
|
||
Vector3 v = Vector3.Lerp(prePos, nextPos, t);
|
||
Vector3 nextPosition = new Vector3((float)Math.Round(v.x, 2), (float)Math.Round(v.y, 2), (float)Math.Round(v.z, 2));
|
||
if (!nextPosition.Equals(transform.localPosition))
|
||
{
|
||
transform.localPosition = nextPosition;
|
||
//Camera.main.transform.localPosition = nextPosition;
|
||
}
|
||
yield return new WaitForEndOfFrame();
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
}
|