This commit is contained in:
wuyanchen 2025-12-31 20:57:14 +08:00
parent a6b48f0e3c
commit 94770ceca3
5 changed files with 131 additions and 25 deletions

View File

@ -20,7 +20,7 @@ namespace XNet.Business.Entity
/// <summary>
/// 是否碰到非地面障碍物或者碰到其他对象,需要转向
/// </summary>
public bool IsNeedToOtherDir { get; set; } = false;
public bool IsNeedToOtherDir { get; set; } = true;
/// <summary>
/// 是否可以控制动作
@ -44,7 +44,7 @@ namespace XNet.Business.Entity
/// <summary>
/// 上一次位置计时的时候的时间用来判断AI是否走死
/// </summary>
public DateTime PrevToDirTime { get; set; } = DateTime.MinValue;
public DateTime? PrevToDirTime { get; set; } = null;
/// <summary>
/// 临时存储的位置

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Numerics;
using XNet.Business.Net;
using XNet.Business.PathNavigation;
using XNet.Business.Tank.Manager;
namespace XNet.Business
{
@ -11,14 +12,16 @@ namespace XNet.Business
private readonly NavMeshManager _navMeshManager;
private readonly SceneAgent _sceneAgent;
private readonly WsConnectionManager _wsManager; // 新增WebSocket管理器
private readonly AIManager _aiManager;
private const int TARGET_FPS = 30;
private const int FRAME_TIME_MS = 1000 / TARGET_FPS;
public GameLoopService(NavMeshManager navMeshManager, SceneAgent sceneAgent, WsConnectionManager wsManager)
public GameLoopService(NavMeshManager navMeshManager, SceneAgent sceneAgent, WsConnectionManager wsManager, AIManager aiManager)
{
_navMeshManager = navMeshManager;
_sceneAgent = sceneAgent;
_wsManager = wsManager;
_aiManager = aiManager;
}
@ -43,27 +46,10 @@ namespace XNet.Business
}
}
private async Task ControlAIPlayer()
{
foreach (var rooms in _wsManager.InstanceSubscribers.Values)
{
var room = rooms.FirstOrDefault().Value;
if (room != null)
{
var template = _navMeshManager.GetNavTemplate(room.MapKey);
if (template == null) return;
var min = template.Bmin;
var max = template.Bmax;
foreach(var player in room.Players)
{
}
}
}
}
private async Task SyncAllMessageToClient()
{
await _aiManager.ControlAIPlayer();
await SendSyncLocationMessage();
}

View File

@ -167,9 +167,8 @@ namespace XNet.Business.PathNavigation
return true;
}
public NavMeshTemplate? GetNavTemplate(string roomId)
public NavMeshTemplate? GetNavTemplate(string templateId)
{
if (!_instanceMap.TryGetValue(roomId, out var templateId)) return null;
if (!_templates.TryGetValue(templateId, out var template)) return null;
return template;

View File

@ -0,0 +1,117 @@
using System;
using System.Numerics;
using XNet.Business.Entity;
using XNet.Business.Net;
using XNet.Business.PathNavigation;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace XNet.Business.Tank.Manager
{
public class AIManager
{
private readonly NavMeshManager _navMeshManager;
private readonly SceneAgent _sceneAgent;
private readonly WsConnectionManager _wsManager;
public AIManager(NavMeshManager navMeshManager, SceneAgent sceneAgent, WsConnectionManager wsManager)
{
_navMeshManager = navMeshManager;
_sceneAgent = sceneAgent;
_wsManager = wsManager;
}
private void MoveAIPlayer(string roomId, ControlPlayer player, Vector3 min, Vector3 max)
{
bool isNeedToMove = false;
DateTime now = DateTime.Now;
if (player.IsNeedToOtherDir)//间隔时间大于3s转向一次
{
var random = new Random();
if (player.PrevToDirTime == null)
{
player.PrevToDirTime = now;
isNeedToMove = true;
}
else
{
//概率再移动
if (random.NextDouble() < 0.95)
{
//在概率范围内,经过指定时间再移动
if ((now - player.PrevToDirTime).Value.TotalMilliseconds > 2000)
{
isNeedToMove = true;
}
}
}
if (isNeedToMove)
{
//地形四边边缘坐标限制
float x1 = min.X * 1.1f;
float x2 = max.X * 0.9f;
float z1 = min.Z * 1.1f;
float z2 = max.Z * 0.9f;
float xMoveTo = (float)random.NextDouble() * x2;
xMoveTo = Math.Clamp(xMoveTo, x1, x2);
if (random.NextDouble() < 0.5)
{
xMoveTo *= -1;
}
float zMoveTo = (float)random.NextDouble() * z2;
zMoveTo = Math.Clamp(zMoveTo, z1, z2);
if (random.NextDouble() < 0.5)
{
zMoveTo *= -1;
}
var gotoPt = _navMeshManager.GetClosetPoint(roomId, new Vector3(xMoveTo, player.CurrentPos.Y, zMoveTo), out long startRef);
if (gotoPt != null)
{
_sceneAgent.AgentGoto(roomId, player.Agent.idx, gotoPt.Value);
}
player.IsNeedToOtherDir = false;
player.PrevToDirTime = now;
}
}
else
{
if (player.PrevToDirTime == null)
{
player.PrevToDirTime = now;
}
var time = (now - player.PrevToDirTime!.Value).TotalMilliseconds;
if (time > 3000)
{
player.IsNeedToOtherDir = true;
player.PrevToDirTime = now;
}
}
}
public async Task ControlAIPlayer()
{
foreach (var rooms in _wsManager.InstanceSubscribers.Values)
{
var room = rooms.FirstOrDefault().Value;
if (room != null && room.StartGameTime != null)
{
var template = _navMeshManager.GetNavTemplate(room.MapKey);
if (template == null) return;
var min = template.Bmin;
var max = template.Bmax;
foreach (var player in room.Players.Values)
{
if (player.IsCanControl && !player.IsAI)
{
continue;
}
MoveAIPlayer(room.RoomId, player, min, max);
}
}
}
}
}
}

View File

@ -3,6 +3,7 @@ using XNet.Business;
using XNet.Business.Manager;
using XNet.Business.Net;
using XNet.Business.PathNavigation;
using XNet.Business.Tank.Manager;
var builder = WebApplication.CreateBuilder(args);
@ -27,17 +28,20 @@ builder.Services.AddCors(c =>
builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
//注入游戏地图导航服务
// 1. 注册 MapAgent 为单例
// 1. 注册 各地图服务 为单例
builder.Services.AddSingleton<NavMeshManager>();
builder.Services.AddSingleton<SceneAgent>();
builder.Services.AddSingleton<WsConnectionManager>();
builder.Services.AddSingleton<AIManager>();
// 2. 注册 SimulationLoop 为 HostedService (后台运行)
builder.Services.AddHostedService<GameLoopService>();
var app = builder.Build();
//使用跨域方案
app.UseCors("Policy");
// 提供 wwwroot 目录中的文件