using Assets.Scenes.Ride.Scripts; using Assets.Scenes.Ride.Scripts.Model; using Assets.Scenes.Ride.Scripts.Model.CyclingModels; using Assets.Scripts.Apis; using Assets.Scripts.Apis.Models; using Assets.Scripts.UI.Prefab.Device; using GeoJSON.Net.Geometry; using Mapbox.Utils; using RenderHeads.Media.AVProVideo; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using TurfCS; using UnityEngine; using DG.Tweening; using Assets.Scenes.Ride.Scripts.Model.RiderModels; using UnityEngine.EventSystems; using UnityEngine.Events; using Helper = Assets.Scenes.Ride.Scripts.Helper; using System.Collections; namespace Assets.Scripts.Scenes.VideoRide { public class CustomRange { public int KeyFrame { get; set; }//帧数 public int Start { get; set; }//起始帧 public int End { get; set; }//结束帧 public float PositionX { get; set; } public float PositionY { get; set; } public float PositionZ { get; set; } public float RotationY { get; set; } public float RotationZ { get; set; } public float LightY { get; set; } private const float defaultRotationY = 0; private const float defaultRotationZ = 0; private const float defaultPositionX = 0; private const float defaultPositionY = -1; private const float defaultPositionZ = 3; private const float defaultLightY = 0; public static CustomRange Read(string s) { // - 代表默认值 //KeyFrame:RotationY,RotationZ,PositionX,PositionY,PositionZ,LightY var result = s.Split(':'); var v = result[1].Split(','); CustomRange r = new CustomRange() { KeyFrame = Convert.ToInt32(result[0]), RotationY = v[0].Equals("-") ? defaultRotationY : (float)Convert.ToDouble(v[0]), RotationZ = v[1].Equals("-") ? defaultRotationZ : (float)Convert.ToDouble(v[1]), PositionX = v[2].Equals("-") ? defaultPositionX : (float)Convert.ToDouble(v[2]), PositionY = v[3].Equals("-") ? defaultPositionY : (float)Convert.ToDouble(v[3]), PositionZ = v[4].Equals("-") ? defaultPositionZ : (float)Convert.ToDouble(v[4]), LightY = v[5].Equals("-") ? defaultLightY : (float)Convert.ToDouble(v[5]), }; return r; } } public class VideoGameManager : DeviceServiceMonoBase { public AbstractVideoPlayer CurrentPlayer { get; set; } private MapDataModel mapData { get; set; } private MediaPlayer mediaPlayer { get; set; } private bool isStart { get; set; } private Route route { get; set; } public MapRoute mapRoute { get; set; } public RouteResultParam selectParamModel; public RouteResult routeResult; public string recordId { get; set; } public DateTime startTime { get; set; } public int FrameKM { get; private set; }//一米对应的帧数 public CyclingModel cyclingModel; public BaseCycling cyclingController; public List mockDirection = new List(); private double endDistance { get; set; } public List slots = new List(); GameObject infoPanel; GameObject OnlinePlayer; GameObject OnlinePlayer_NV; private bool isStop { get; set; } public enum ARMode { RIDE, INSPECT } //当前是观察者视角还是骑行者视角 public ARMode _aRMode { get; set; } public int RankingId { get; set; } private int currentOnlineCount = 0; private void Awake() { base.Awake(); mediaPlayer = FindObjectOfType(); DeviceCache.Init(PFConstants.DeviceCacheFolder); var check = CheckAnt();//初始化蓝牙设备 recordId = Guid.NewGuid().ToString(); MockDirection(); infoPanel = Resources.Load("UI/Prefab/AR/VideoPlayerHead"); OnlinePlayer = Resources.Load("UI/Prefab/AR/OnlineVideoPlayer"); OnlinePlayer_NV = Resources.Load("UI/Prefab/AR/OnlineVideoPlayer_NV"); StartCoroutine(Init()); } void Start() { InitSlots(); UIManager.Instance.MainPanel = GetCanvasTransform().GetComponent(); UIManager.Instance.ModalsPanel = this.transform.Find("Canvas/ModalPanel").GetComponent(); var routeId = App.RouteIdParam; //获取路书 GetMapRoute(); GetMapData(); route = new Route(mapData, mapRoute); if (selectParamModel == null) { selectParamModel = new RouteResultParam { CompetitionId = 0, ContinueIndex = 0, ContinueMark = "", GlobalContinue = false, EndDistance = 0, RankingsId = new List(), OnlineUserId = 0, RouteId = routeId }; } endDistance = 0; videoLoading = FindObjectOfType(); } private IEnumerator Init() { yield return null; videoLoading.Init();//初始化loading页面 } public int GetFrameKm() { var s = mediaPlayer.Info.GetDurationFrames(); FrameKM = (int)Math.Floor(s / (mapData.TotalDistance * 1000)); return FrameKM; } VideoLoading videoLoading { get; set; } // Update is called once per frame float timer = 0f; void Update() { timer -= Time.deltaTime; while (timer <= 0) { if (cyclingController == null) return; cyclingController.Run(null); var onlineRiders = cyclingController.riders; try { CreateOnlineUser(onlineRiders); } catch (Exception e) { Debug.Log(e.Message); } timer += 1f; } } MapRouteRankingList rankingList; public MapRouteRankingList GetRankingList(string type = "month") { var endTime = UIManager.Now.GetDateTime(); var startTime = Helper.GetStartTime(endTime, type); var list = ConfigHelper.mapApi.GetRouteRanking(App.RouteIdParam, "1", 0, 10, "", startTime,endTime); if (list.result) { rankingList = list.data; } return rankingList; } //创建当前线路其他选手 private void CreateOnlineUser(List list) { //观察者模式随机选择当前骑行的人作为观察对象 if (_aRMode == ARMode.INSPECT && CurrentPlayer == null) { var currentPlayer = list.FirstOrDefault(); if (currentPlayer != null) { var onlineRider = currentPlayer as OnlineRiderModel; GameObject obj; if (onlineRider.Sex == 1) { obj = Instantiate(OnlinePlayer, transform); } else { obj = Instantiate(OnlinePlayer_NV, transform); } var online = obj.GetComponent(); online.SetPlayer(onlineRider.NickName, onlineRider.EndDistance-onlineRider.PreDistance, onlineRider.EndDistance, onlineRider.Cadence, onlineRider.HeartRate, onlineRider.TotalTicks, onlineRider.UserId, onlineRider.Power, onlineRider.EndDistance,onlineRider.FrameRate.Value); obj.transform.DOMoveX(0, 1); ChangePlayer(currentPlayer.UserId); CurrentPlayer = online; } } //list = list.Where(c => c.UserId != CurrentPlayer?.UserId).ToList(); var onlinelist = MapUDPService.GetOnlineUsers(App.RouteIdParam);//最新列表 var currentPlayerInfo = onlinelist.Where(c => c.Id == CurrentPlayer.UserId).FirstOrDefault(); var players = FindObjectsOfType(); //移除 foreach (var item in players) { var currentItem = list.Where(c => c.UserId == item.UserId).FirstOrDefault(); if (currentItem == null) { item.Destroy(); if (currentOnlineCount > 1) { currentOnlineCount--; } } } //新增/更新 var pagedList = list.Take(5).ToList(); foreach (var item in pagedList) { var onlineRider = item as OnlineRiderModel; var exsit = players.Where(c => c.UserId == item.UserId).FirstOrDefault(); if (exsit != null) { var online = exsit.GetComponent(); online.SetPlayer(onlineRider.NickName, onlineRider.EndDistance - onlineRider.PreDistance, onlineRider.EndDistance, item.Cadence, item.HeartRate, onlineRider.WeightKg, item.UserId, onlineRider.Power, currentPlayerInfo.EndDistance, onlineRider.FrameRate.Value); } else { if (currentPlayerInfo != null) { var diff = item.EndDistance % mapData.TotalDistance - currentPlayerInfo.EndDistance % mapData.TotalDistance; if (Math.Abs(diff * 1000) <= 100) { GameObject obj; if (currentPlayerInfo.Sex == 1) { obj = Instantiate(OnlinePlayer, transform); } else { obj = Instantiate(OnlinePlayer_NV, transform); } var online = obj.GetComponent(); online.SetPlayer(onlineRider.NickName, onlineRider.EndDistance - onlineRider.PreDistance, onlineRider.EndDistance, item.Cadence, item.HeartRate, onlineRider.WeightKg, item.UserId, onlineRider.Power, currentPlayerInfo.EndDistance, onlineRider.FrameRate.Value); obj.transform.DOMoveX(slots[currentOnlineCount], 1); currentOnlineCount++; } } } } } //设置当前是骑行模式还是观察模式 public void SetCurrentMode(ARMode aRMode) { _aRMode = aRMode; var uimanager = FindObjectOfType(); uimanager.SetCurrentMode(aRMode); if (_aRMode != ARMode.INSPECT) { InitCurrentPlayer(); ContinueAsync(); } } //初始化人物模型 public void InitCurrentPlayer() { GameObject videoPlayer; if (App.CurrentUser.Sex == 1) { videoPlayer = Instantiate(Resources.Load("UI/Prefab/AR/VidePlayer"),transform); } else { videoPlayer = Instantiate(Resources.Load("UI/Prefab/AR/VidePlayer_NV"), transform); } CurrentPlayer = videoPlayer.GetComponent(); } Vector3 origin = new Vector3(0, -1, 3); //切换人物 public void ChangePlayer(int userId) { var players = FindObjectsOfType(); if (players != null) { CurrentPlayer = players.Where(c => c.UserId == userId).FirstOrDefault(); if (CurrentPlayer != null) { var currentFrames = DistanceToFrames(CurrentPlayer.totalDistance); //获取当前人物的帧数 SetCurrentFrame(currentFrames); //设置人物的位置 CurrentPlayer.transform.position = origin; } else { var onlineRiders = cyclingController.riders; var currentPlayer = onlineRiders.Where(c => c.UserId == userId).FirstOrDefault(); if (currentPlayer != null) { var onlineRider = currentPlayer as OnlineRiderModel; var obj = Instantiate(OnlinePlayer, transform); var online = obj.GetComponent(); online.SetPlayer(onlineRider.NickName, onlineRider.EndDistance-onlineRider.PreDistance, onlineRider.EndDistance, onlineRider.Cadence, onlineRider.HeartRate, 0, onlineRider.UserId, onlineRider.Power, onlineRider.EndDistance, onlineRider.FrameRate.Value); obj.transform.position = origin; CurrentPlayer = online; var currentFrames = DistanceToFrames(CurrentPlayer.totalDistance); //获取当前人物的帧数 SetCurrentFrame(currentFrames); } var clearList = players.Where(c => c.UserId != userId); foreach (var item in clearList) { item.Destroy(); } } } } //初始化骑行选手横向插槽 private void InitSlots() { for (float i = -2f; i <= 2; i+=0.5f) { if (i != 0) { slots.Add(i); } } slots = slots.OrderBy(c => Math.Abs(c)).ToList(); } //获取角色头顶信息预制件 public GameObject GetHeadInfo() { return infoPanel; } //骑行记录 public void ContinueAsync() { //骑行结果 if (App.routeResult != null) { routeResult = App.routeResult; selectParamModel = App.routeResult.ContinueCyclingParam;//继续骑行 SetCyclingModel(CyclingModel.Single); endDistance = routeResult.EndDistance; SetCurrentFrame(routeResult.LastFrame ?? 0); var v = FindObjectOfType(); v.SetEndDistance(endDistance); } } private void MockDirection() { var text = Resources.Load("UI/direction"); var arr = text.text.Replace("\r\n", " ").Split(' '); foreach (var item in arr) { if (string.IsNullOrEmpty(item)) continue; var c = CustomRange.Read(item); mockDirection.Add(c); } } //设置骑行模式 public void SetCyclingModel(CyclingModel mode) { startTime = UIManager.Now.GetDateTime(); this.cyclingModel = mode; switch (cyclingModel) { case CyclingModel.Single: cyclingController = new SingleModel(route, selectParamModel); break; case CyclingModel.GameRoom: cyclingController = new GameModel(route, selectParamModel); break; } } //模拟登录 private async Task Login() { var result = await new UserApi().Login("anyway2019@163.com", "123456", ""); App.CurrentUser = result.data; } //开始游戏 public void StartGame() { if (startTime == null) { startTime = UIManager.Now.GetDateTime(); } isStart = true; } //骑行是否开始 public bool IsStart() { return isStart; } //获取UI public Transform GetCanvasTransform() { return transform.Find("Canvas/Panel"); } //设置视频源文件 public void SetMedia(string path) { if (mediaPlayer != null) { mediaPlayer.OpenMedia(new MediaPath(path,MediaPathType.AbsolutePathOrURL),false); } } //是否停止 public bool IsQuit() { return isStop; } //设置当前视频播放速度 public void Play(float playbackRate = 1f) { if (mediaPlayer != null) { mediaPlayer.PlaybackRate = playbackRate; mediaPlayer.Play(); } if (startTime == null) { startTime = UIManager.Now.GetDateTime(); } isStart = true; } //暂停 public void Pause() { isStart = !isStart; isStop = !isStop; if (isStop) { mediaPlayer?.Pause(); } else { mediaPlayer?.Play(); } } //退出骑行 public void Quit() { mediaPlayer?.Pause();//暂停视频 isStart = false; isStop = true; mediaPlayer?.Stop(); } //获取当前视频帧数 public int GetCurrentFrame() { return mediaPlayer.Control.GetCurrentTimeFrames(); } //距离换算成视频帧数 public int DistanceToFrames(double distance) { var frames = mediaPlayer.Info.GetMaxFrameNumber(); //视频打开后生效 var TotalDistance = route?.TotalDistance ?? 0; distance %= TotalDistance; return TotalDistance > 0 ? Convert.ToInt32(frames / TotalDistance * distance) : 0; } public int GetFrameOffSet(double distance) { var currentFrame = GetCurrentFrame(); var frames = mediaPlayer.Info.GetMaxFrameNumber(); //视频打开后生效 var TotalDistance = route?.TotalDistance ?? 0; TotalDistance *= 1000; distance %= TotalDistance; var offset = TotalDistance > 0 ? Convert.ToInt32(frames / TotalDistance * distance) : 0; return currentFrame + offset; } //设置当前视频播放进度 public void SetCurrentFrame(int seq) { mediaPlayer?.Control.SeekToFrame(seq); } //获取路书gps信息 public MapDataModel GetMapData() { if (mapData == null) { var mapApi = ConfigHelper.mapApi; int routeId = App.RouteIdParam; mapData = mapApi.GetData(routeId);//获取路书地理数据 } return mapData; } //获取路书信息 public MapRoute GetMapRoute() { if (mapRoute == null) { var mapApi = ConfigHelper.mapApi; int routeId = App.RouteIdParam; mapRoute = mapApi.GetById(routeId).data; } return mapRoute; } //保存骑行记录 public void Save(double totalDistance) { Quit(); cyclingController.recorderData.EndTime = UIManager.Now.GetDateTime(); var path = PFConstants.MapWorkoutRecordFolder + "/" + recordId; Assets.Scenes.Ride.Scripts.Helper.CreateDirectoryIfNotExsit(path); string imageFileName = path + "/" + Guid.NewGuid().ToString() + ".png"; CaptureCamera(Camera.main, new Rect(Screen.width * 0f, Screen.height * 0f, Screen.width * 0.5f, Screen.height * 0.5f), imageFileName); cyclingController.recorderData.StartTime = startTime; cyclingController.recorderData.IsCompleted = totalDistance >= mapData.TotalDistance; cyclingController.recorderData.EndDistance = totalDistance; cyclingController.recorderData.AntModelId = AntModelId; cyclingController.recorderData.ManufacturerId = ManufacturerId; cyclingController.recorderData.ManufacturerName = ManufacturerName; cyclingController.recorderData.DeviceNumber = DeviceNumber; cyclingController.recorderData.LastFrame = GetCurrentFrame(); RankingId = cyclingController.recorderData.SaveWithLocalRecordAysnc(cyclingModel, selectParamModel, imageFileName, recordId, path); } public void AddEvent(GameObject sender, EventTriggerType eventType, UnityAction unityAction) { UIManager.AddEvent(sender, eventType, unityAction); } public Texture GetCountryImageByCode(string code) { return UIManager.Instance.loginRegOptions.GetCountryImage(code); } public Texture GetCountryImageByName(string name) { return UIManager.Instance.loginRegOptions.GetCountryImageByName(name); } //截图 protected void CaptureCamera(Camera camera, Rect rect, string fileName) { byte[] bytes = CaptureCameraReturnByte(camera, rect); System.IO.File.WriteAllBytes(fileName, bytes); } private byte[] CaptureCameraReturnByte(Camera camera, Rect rect) { RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, 0); camera.targetTexture = rt; camera.Render(); RenderTexture.active = rt; Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24, false); screenShot.ReadPixels(rect, 0, 0); screenShot.Apply(); camera.targetTexture = null; RenderTexture.active = null; GameObject.Destroy(rt); return screenShot.EncodeToJPG(); } //根据距离计算下一个点坐标 public 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); } else { return new Vector2d(0, 0); } } private void OnDestroy() { App.gameRoomDetail = null; } } }