Commit e4c4bde0 authored by haiyoucuv's avatar haiyoucuv

init

parent aa7315ad
......@@ -29,26 +29,26 @@ export class AISnake extends Snake {
private escapeTarget: Snake = null;
private readonly BASE_VIEW_DISTANCE = 300;
private readonly INTERCEPT_DISTANCE = 400;
private readonly PREDICTION_TIME = 1.0;
private readonly ESCAPE_BOUNDARY = 200;
private readonly INTERCEPT_DISTANCE = 350; // 降低拦截距离
private readonly PREDICTION_TIME = 1.2; // 增加预测时间
private readonly ESCAPE_BOUNDARY = 250; // 增加边界安全距离
private readonly SAFE_MARGIN = 3.0; // 增加安全边际
private readonly COLLISION_CHECK_DISTANCE = 500; // 增加碰撞检测距离
private readonly ASSIST_DISTANCE = 500; // 协助攻击的最大距离
private readonly SAFE_MARGIN = 2; // 安全边际系数
private readonly COLLISION_CHECK_DISTANCE = 400; // 增加碰撞检测距离
private readonly BODY_AVOID_MARGIN = 2.5; // 增加身体避让边际
private readonly DANGER_ANGLE_THRESHOLD = 75; // 扩大危险角度范围
private readonly DANGER_ANGLE_THRESHOLD = 90; // 扩大危险角度范围
private readonly COUNTER_ATTACK_THRESHOLD = 0.8; // 反击判定阈值
private readonly SAFE_DISTANCE_MULTIPLIER = 1.5; // 安全距离倍数
private assistTarget: AISnake = null; // 正在协助的AI蛇
private get difficultyParams() {
return {
reactionTime: math.lerp(0.8, 0.2, (this.difficulty - 1) / 4),
viewDistance: this.BASE_VIEW_DISTANCE * (1 + (this.difficulty - 1) * 0.2),
interceptDistance: this.INTERCEPT_DISTANCE * (1 + (this.difficulty - 1) * 0.2),
aggressiveness: math.lerp(0.3, 0.9, (this.difficulty - 1) / 4),
predictionAccuracy: math.lerp(0.5, 0.9, (this.difficulty - 1) / 4),
turnSpeed: math.lerp(2, 5, (this.difficulty - 1) / 4)
aggressiveness: math.lerp(0.2, 0.7, (this.difficulty - 1) / 4), // 降低激进程度
predictionAccuracy: math.lerp(0.6, 0.9, (this.difficulty - 1) / 4),
turnSpeed: math.lerp(2, 4.5, (this.difficulty - 1) / 4) // 略微降低最大转向速度
};
}
......@@ -231,6 +231,7 @@ export class AISnake extends Snake {
}
private executeCurrentState(dt: number) {
const threat = this.findNearbyAIToAvoid();
if (threat && threat.dangerLevel > 30) { // 降低触发避让的阈值
......@@ -295,49 +296,6 @@ export class AISnake extends Snake {
return this.head.angle;
}
// 计算最佳躲避角度
private calculateAvoidanceAngle(threat: Snake): number {
const myPos = this.head.getPosition();
const threatPos = threat.head.getPosition();
const baseEscapeAngle = this.calculateEscapeAngle(threatPos);
// 尝试多个躲避角度
const angles = [
baseEscapeAngle,
baseEscapeAngle + 45,
baseEscapeAngle - 45,
baseEscapeAngle + 90,
baseEscapeAngle - 90
];
// 选择最安全的角度
let bestAngle = baseEscapeAngle;
let maxSafety = -1;
for (const angle of angles) {
const futurePos = this.predictFuturePosition(myPos, angle, this.speed * 3);
let safety = Vec3.distance(futurePos, threatPos);
// 检查这个角度是否会导致撞墙
if (this.willHitBoundary(angle)) {
continue;
}
// 检查与威胁物的身体的距离
for (const bodyPart of threat.bodyArr) {
const bodyDist = Vec3.distance(futurePos, bodyPart.getPosition());
safety = Math.min(safety, bodyDist);
}
if (safety > maxSafety) {
maxSafety = safety;
bestAngle = angle;
}
}
return bestAngle;
}
private canInterceptPlayer(player: Snake): boolean {
if (!player || !player.isLife) return false;
......@@ -346,10 +304,10 @@ export class AISnake extends Snake {
const playerPos = player.head.getPosition();
const distance = Vec3.distance(myPos, playerPos);
// 降低拦截距离和提高长度要求
return distance < params.interceptDistance * 0.8 && // 减小拦截距离
this.getSnakeLen() > player.getSnakeLen() * 0.8 && // 提高长度要求
math.random() < params.aggressiveness * 0.8; // 降低激进程度
return distance < params.interceptDistance * 0.7 && // 进一步减小拦截距离
this.getSnakeLen() > player.getSnakeLen() * 1.2 && // 提高长度要求
math.random() < params.aggressiveness * 0.6 && // 降低激进程度
!this.isNearBoundary(playerPos); // 不在边界附近才拦截
}
private executeHunting() {
......@@ -357,24 +315,82 @@ export class AISnake extends Snake {
const myPos = this.head.getPosition();
const distance = Vec3.distance(myPos, this.targetFood);
// 计算到食物的直接角度
const targetAngle = this.calculateTargetAngle(this.targetFood);
const angleDiff = Math.abs(this.head.angle - targetAngle);
// 检查是否需要避开自己的身体
const needAvoidBody = this.willHitOwnBody(targetAngle);
if (needAvoidBody) {
// 寻找替代路径
const alternativeAngle = this.findAlternativeAngleToFood(this.targetFood);
this.smoothRotateToAngle(alternativeAngle, this.difficultyParams.turnSpeed);
// 寻找更优的替代路径
const alternativeAngle = this.findBetterAngleToFood(this.targetFood);
if (alternativeAngle !== null) {
this.smoothRotateToAngle(alternativeAngle, this.difficultyParams.turnSpeed * 1.8);
} else {
// 如果没有找到好的替代路径,执行更激进的转向
this.executeAggressiveTurn(targetAngle);
}
this.isFast = false;
} else {
// 直接前进
this.smoothRotateToAngle(targetAngle, this.difficultyParams.turnSpeed * 1.5);
// 根据角度差决定转向策略
if (angleDiff > 90) {
// 大角度差时执行快速转向
this.executeAggressiveTurn(targetAngle);
this.isFast = false;
} else {
// 小角度差时正常追逐
this.smoothRotateToAngle(targetAngle, this.difficultyParams.turnSpeed * 1.5);
this.isFast = distance < this.BASE_VIEW_DISTANCE / 2;
}
}
}
// 更激进的转向方法
private executeAggressiveTurn(targetAngle: number) {
const currentAngle = this.head.angle;
let angleDiff = targetAngle - currentAngle;
// 标准化角度差到 -180 到 180 度范围
while (angleDiff > 180) angleDiff -= 360;
while (angleDiff < -180) angleDiff += 360;
// 使用更大的转向速度
const turnSpeed = this.difficultyParams.turnSpeed * 2.5;
this.head.angle += math.clamp(angleDiff, -turnSpeed, turnSpeed);
}
// 寻找更好的替代角度
private findBetterAngleToFood(foodPos: Vec3): number | null {
const myPos = this.head.getPosition();
const directAngle = this.calculateTargetAngle(foodPos);
const currentDistance = Vec3.distance(myPos, foodPos);
// 根据当前角度差决定搜索范围
const angleDiff = Math.abs(this.head.angle - directAngle);
const searchRange = angleDiff > 90 ? [-120, -90, -60, -45, -30, 30, 45, 60, 90, 120]
: [-60, -45, -30, -15, 15, 30, 45, 60];
let bestAngle = null;
let bestImprovement = 0;
for (const offset of searchRange) {
const testAngle = directAngle + offset;
if (this.willHitOwnBody(testAngle)) continue;
// 根据距离和路径调整速度
this.isFast = distance < this.BASE_VIEW_DISTANCE / 2 && !needAvoidBody;
const futurePos = this.predictFuturePosition(myPos, testAngle, this.radius * 5);
const newDistance = Vec3.distance(futurePos, foodPos);
// 计算路径改善程度
const improvement = currentDistance - newDistance;
// 选择最佳改善路径
if (improvement > bestImprovement) {
bestImprovement = improvement;
bestAngle = testAngle;
}
}
return bestAngle;
}
// 检查是否会撞到自己的身体
......@@ -394,32 +410,6 @@ export class AISnake extends Snake {
return false;
}
// 寻找到食物的替代角度
private findAlternativeAngleToFood(foodPos: Vec3): number {
const myPos = this.head.getPosition();
const directAngle = this.calculateTargetAngle(foodPos);
// 尝试不同的角度偏移
const offsets = [30, -30, 45, -45, 60, -60];
for (const offset of offsets) {
const testAngle = directAngle + offset;
if (!this.willHitOwnBody(testAngle)) {
// 检查这个角度是否会让我们更接近食物
const futurePos = this.predictFuturePosition(myPos, testAngle, this.radius * 4);
const currentDistance = Vec3.distance(myPos, foodPos);
const futureDistance = Vec3.distance(futurePos, foodPos);
if (futureDistance < currentDistance) {
return testAngle;
}
}
}
// 如果没找到更好的角度,返回直接角度
return directAngle;
}
private executeIntercepting() {
if (!this.targetSnake || !this.targetSnake.isLife) return;
......@@ -433,12 +423,11 @@ export class AISnake extends Snake {
// 增加躲避权重
const aiDistance = Vec3.distance(myPos, nearbyAI.snake.head.getPosition());
const avoidWeight = math.clamp(1 - aiDistance / (this.BASE_VIEW_DISTANCE * 0.5), 0.3, 0.9); // 提高最小权重
const avoidWeight = math.clamp(1 - aiDistance / (this.BASE_VIEW_DISTANCE * 0.6), 0.4, 0.95); // 增加避让权重
const finalAngle = this.blendAngles(targetAngle, avoidAngle, avoidWeight);
// 使用更快的转向速度进行躲避
this.smoothRotateToAngle(finalAngle, this.difficultyParams.turnSpeed * 1.8);
this.isFast = false; // 躲避时降低速度
this.smoothRotateToAngle(finalAngle, this.difficultyParams.turnSpeed * 1.5);
this.isFast = false; // 避让时始终降速
} else {
const predictedPos = this.predictTargetPosition(this.targetSnake);
const targetAngle = this.calculateTargetAngle(predictedPos);
......@@ -471,12 +460,15 @@ export class AISnake extends Snake {
}
// 寻找需要躲避的附近AI
private findNearbyAIToAvoid(): { snake: Snake, dangerLevel: number } | null {
private findNearbyAIToAvoid(): { snake: Snake, dangerLevel: number, canCounter: boolean } | null {
const myPos = this.head.getPosition();
const myFuturePos = this.predictFuturePosition(myPos, this.head.angle, this.speed * 2);
let maxDanger = 0;
let mostDangerousSnake = null;
const myLength = this.getSnakeLen();
let canCounterAttack = false;
const allSnakes = [...MainGame.ins.animalNode.children, MainGame.ins.player.node]
.map(node => node.getComponent(Snake))
.filter(snake => snake && snake !== this && snake.isLife);
......@@ -484,15 +476,25 @@ export class AISnake extends Snake {
for (const snake of allSnakes) {
let snakeDanger = 0;
const headDistance = Vec3.distance(myPos, snake.head.getPosition());
const isPlayer = snake === MainGame.ins.player;
// 对玩家增加额外的危险系数
const playerDangerMultiplier = isPlayer ? 1.5 : 1.0;
// 检查头部威胁
if (headDistance < this.COLLISION_CHECK_DISTANCE) {
const angleToHead = this.calculateTargetAngle(snake.head.getPosition());
const headAngleDiff = Math.abs(this.head.angle - angleToHead);
if (headAngleDiff < this.DANGER_ANGLE_THRESHOLD) {
snakeDanger = Math.max(snakeDanger,
(this.COLLISION_CHECK_DISTANCE - headDistance) / this.COLLISION_CHECK_DISTANCE * 100);
// 对玩家扩大危险角度阈值
const effectiveAngleThreshold = isPlayer ?
this.DANGER_ANGLE_THRESHOLD * 1.2 :
this.DANGER_ANGLE_THRESHOLD;
if (headAngleDiff < effectiveAngleThreshold) {
const baseDanger = (this.COLLISION_CHECK_DISTANCE - headDistance) /
this.COLLISION_CHECK_DISTANCE * 100;
snakeDanger = Math.max(snakeDanger, baseDanger * playerDangerMultiplier);
}
}
......@@ -502,21 +504,29 @@ export class AISnake extends Snake {
const bodyDistance = Vec3.distance(myPos, bodyPart.getPosition());
const futureDist = Vec3.distance(myFuturePos, bodyPart.getPosition());
// 计算与身体部分的相对运动
const bodyAngle = this.calculateTargetAngle(bodyPart.getPosition());
const angleDiff = Math.abs(this.head.angle - bodyAngle);
// 根据距离和角度计算危险程度
if (bodyDistance < this.COLLISION_CHECK_DISTANCE && angleDiff < this.DANGER_ANGLE_THRESHOLD) {
const distanceDanger = (this.COLLISION_CHECK_DISTANCE - bodyDistance) / this.COLLISION_CHECK_DISTANCE;
// 对玩家的身体也使用更大的检测范围
const effectiveCheckDistance = isPlayer ?
this.COLLISION_CHECK_DISTANCE * 1.2 :
this.COLLISION_CHECK_DISTANCE;
if (bodyDistance < effectiveCheckDistance && angleDiff < this.DANGER_ANGLE_THRESHOLD) {
const distanceDanger = (effectiveCheckDistance - bodyDistance) / effectiveCheckDistance;
const angleDanger = (this.DANGER_ANGLE_THRESHOLD - angleDiff) / this.DANGER_ANGLE_THRESHOLD;
const futureDanger = futureDist < bodyDistance ? 1.5 : 1; // 如果预测位置更近,增加危险系数
const futureDanger = futureDist < bodyDistance ? 2.5 : 1; // 增加未来碰撞的危险系数
const partDanger = (distanceDanger * angleDanger * futureDanger) * 100;
const partDanger = (distanceDanger * angleDanger * futureDanger) * 120 * playerDangerMultiplier;
snakeDanger = Math.max(snakeDanger, partDanger);
}
}
// 如果是玩家且正在加速,进一步提高危险等级
if (isPlayer && snake.isFast) {
snakeDanger *= 1.3;
}
// 更新最危险的蛇
if (snakeDanger > maxDanger) {
maxDanger = snakeDanger;
......@@ -524,7 +534,22 @@ export class AISnake extends Snake {
}
}
return maxDanger > 0 ? { snake: mostDangerousSnake, dangerLevel: maxDanger } : null;
// 评估是否可以反击
if (mostDangerousSnake) {
const threatLength = mostDangerousSnake.getSnakeLen();
const lengthAdvantage = myLength / threatLength;
const distance = Vec3.distance(myPos, mostDangerousSnake.head.getPosition());
const isSafeDistance = distance > this.COLLISION_CHECK_DISTANCE * this.SAFE_DISTANCE_MULTIPLIER;
canCounterAttack = lengthAdvantage > this.COUNTER_ATTACK_THRESHOLD && isSafeDistance;
}
// 降低触发避让的阈值,使AI更容易进入避让状态
return maxDanger > 15 ? {
snake: mostDangerousSnake,
dangerLevel: maxDanger,
canCounter: canCounterAttack
} : null;
}
private predictFuturePosition(currentPos: Vec3, angle: number, speed: number): Vec3 {
......@@ -535,63 +560,163 @@ export class AISnake extends Snake {
}
private executeEscaping() {
if (!this.escapeTarget) {
// 如果没有特定的逃离目标,检查并避开所有潜在威胁
this.avoidAllThreats();
return;
}
const myPos = this.head.getPosition();
const threatPos = this.escapeTarget.head.getPosition();
const distance = Vec3.distance(myPos, threatPos);
if (this.escapeTarget) {
let escapeAngle = this.calculateEscapeAngle(this.escapeTarget.head.getPosition());
// 紧急避让判定
const isEmergency = distance < this.radius * 3;
const escapeAngle = this.calculateAvoidanceAngle(this.escapeTarget);
// 考虑身体部分的位置来调整逃跑角度
for (const bodyPart of this.escapeTarget.bodyArr) {
const bodyDistance = Vec3.distance(myPos, bodyPart.getPosition());
if (bodyDistance < this.BASE_VIEW_DISTANCE * 0.7) {
const bodyEscapeAngle = this.calculateEscapeAngle(bodyPart.getPosition());
// 综合考虑头部和身体的逃跑角度
escapeAngle = (escapeAngle + bodyEscapeAngle) / 2;
if (isEmergency) {
// 紧急情况:直接设置角度
this.head.angle = escapeAngle;
this.isFast = false;
} else {
// 非紧急情况:快速但平滑地转向
const angleDiff = escapeAngle - this.head.angle;
// 标准化角度差到 -180 到 180 度范围
const normalizedDiff = (angleDiff + 180) % 360 - 180;
this.head.angle += math.clamp(normalizedDiff, -15, 15);
this.isFast = distance > this.radius * 5;
}
}
// 避开所有潜在威胁
private avoidAllThreats() {
const myPos = this.head.getPosition();
const allSnakes = [...MainGame.ins.animalNode.children, MainGame.ins.player.node]
.map(node => node.getComponent(Snake))
.filter(snake => snake && snake !== this && snake.isLife);
let nearestThreatDistance = Infinity;
let bestEscapeAngle = this.head.angle;
let hasThreats = false;
// 检查所有潜在威胁
for (const snake of allSnakes) {
const distance = Vec3.distance(myPos, snake.head.getPosition());
if (distance < this.COLLISION_CHECK_DISTANCE) {
hasThreats = true;
if (distance < nearestThreatDistance) {
nearestThreatDistance = distance;
bestEscapeAngle = this.calculateAvoidanceAngle(snake);
}
}
}
// 检查逃跑方向是否会导致撞墙
if (this.willHitBoundary(escapeAngle)) {
escapeAngle = this.adjustEscapeAngle(escapeAngle);
if (hasThreats) {
// 有威胁时执行避让
const isEmergency = nearestThreatDistance < this.radius * 3;
if (isEmergency) {
this.head.angle = bestEscapeAngle;
this.isFast = false;
} else {
const angleDiff = bestEscapeAngle - this.head.angle;
const normalizedDiff = (angleDiff + 180) % 360 - 180;
this.head.angle += math.clamp(normalizedDiff, -15, 15);
this.isFast = nearestThreatDistance > this.radius * 5;
}
this.smoothRotateToAngle(escapeAngle, this.difficultyParams.turnSpeed * 1.8);
} else {
// 没有威胁时检查边界
this.avoidBoundary();
}
this.isFast = true;
}
private willHitBoundary(angle: number): boolean {
// 计算避让角度的方法,增加紧急情况下的处理
private calculateAvoidanceAngle(threat: Snake): number {
const myPos = this.head.getPosition();
const radian = angle * Math.PI / 180;
const checkDistance = this.ESCAPE_BOUNDARY;
const threatPos = threat.head.getPosition();
const baseEscapeAngle = this.calculateEscapeAngle(threatPos);
const futureX = myPos.x + Math.cos(radian) * checkDistance;
const futureY = myPos.y + Math.sin(radian) * checkDistance;
// 检查基础逃生角度是否安全
if (!this.willHitBoundary(baseEscapeAngle)) {
return baseEscapeAngle;
}
return this.isInDangerousPosition(v3(futureX, futureY, 0));
// 如果基础角度不安全,寻找最佳替代角度
const angles = [
baseEscapeAngle,
baseEscapeAngle + 45, baseEscapeAngle - 45,
baseEscapeAngle + 90, baseEscapeAngle - 90,
baseEscapeAngle + 135, baseEscapeAngle - 135,
baseEscapeAngle + 180
];
let bestAngle = baseEscapeAngle;
let maxSafety = -Infinity;
for (const angle of angles) {
if (this.willHitBoundary(angle)) continue;
const safety = this.evaluateEscapeAngleSafety(angle, threat);
if (safety > maxSafety) {
maxSafety = safety;
bestAngle = angle;
}
}
return bestAngle;
}
private adjustEscapeAngle(originalAngle: number): number {
const adjustAngles = [-45, 45, -90, 90, -135, 135, 180];
// 评估逃生角度的安全性
private evaluateEscapeAngleSafety(angle: number, threat: Snake): number {
const myPos = this.head.getPosition();
const futurePos = this.predictFuturePosition(myPos, angle, this.radius * 4);
let safety = 100;
for (const adjustment of adjustAngles) {
const newAngle = (originalAngle + adjustment) % 360;
if (!this.willHitBoundary(newAngle)) {
return newAngle;
// 检查与威胁的距离
const threatDistance = Vec3.distance(futurePos, threat.head.getPosition());
safety += threatDistance;
// 检查边界距离
const boundaryDist = this.getDistanceToBoundary(futurePos);
safety += boundaryDist * 2;
// 检查与其他蛇的距离
const allSnakes = [...MainGame.ins.animalNode.children, MainGame.ins.player.node]
.map(node => node.getComponent(Snake))
.filter(snake => snake && snake !== this && snake !== threat && snake.isLife);
for (const snake of allSnakes) {
const distance = Vec3.distance(futurePos, snake.head.getPosition());
if (distance < this.COLLISION_CHECK_DISTANCE) {
safety -= (this.COLLISION_CHECK_DISTANCE - distance);
}
}
return originalAngle; // 如果没有找到更好的角度,返回原角度
return safety;
}
private willHitBoundary(angle: number): boolean {
const myPos = this.head.getPosition();
const futurePos = this.predictFuturePosition(myPos, angle, this.radius * 4);
const boundaryDist = this.getDistanceToBoundary(futurePos);
return boundaryDist < this.ESCAPE_BOUNDARY;
}
// 获取到边界的距离
private getDistanceToBoundary(position: Vec3): number {
const mapWidth = Global.MAP_WIDTH;
const mapHeight = Global.MAP_HEIGHT;
return Math.min(
mapWidth / 2 - Math.abs(position.x),
mapHeight / 2 - Math.abs(position.y)
);
}
private executeWandering() {
// 增加方向改变的概率
if (math.randomRangeInt(0, 20) == 0) { // 减少方向改变的频率
if (math.randomRangeInt(0, 30) == 0) {
const direction = math.randomRangeInt(0, 3);
const speed = math.randomRangeInt(1, 4);
const speed = math.randomRangeInt(1, 3);
if (direction === DirectionType.LEFT) {
this.head.angle += speed;
......@@ -601,7 +726,7 @@ export class AISnake extends Snake {
}
// 减少速度变化的频率
this.isFast = math.random() < this.difficultyParams.aggressiveness * 0.1;
this.isFast = math.random() < this.difficultyParams.aggressiveness * 0.05;
}
private avoidBoundary() {
......@@ -762,7 +887,6 @@ export class AISnake extends Snake {
return score;
}
// 判断是否能在竞争者之前到达食物
private canReachFoodFirst(foodPos: Vec3, myDistance: number, competitors: Snake[]): boolean {
const mySpeed = this.speed * (this.isFast ? 2 : 1);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment