欢迎光临
我们一直在努力

坦克大战游戏开发实战(七)-敌方AI系统设计与实现

坦克大战游戏开发实战(七)-敌方AI系统设计与实现

前言

在坦克大战游戏中,敌方AI系统是游戏挑战性的核心来源。优秀的AI设计能够让游戏既具有挑战性,又不会让玩家感到过于困难。本文将详细讲解敌方AI系统的设计与实现,包括敌方坦克类型、AI行为决策、生成系统等核心内容。

敌方AI系统的重要性

  • 游戏挑战:提供合理的游戏难度曲线
  • 智能行为:模拟真实的战术决策
  • 多样体验:不同类型敌方带来不同体验
  • 动态平衡:根据玩家表现动态调整
  • #mermaid-svg-4DhaxVJjJeT6HEtE{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4DhaxVJjJeT6HEtE .error-icon{fill:#552222;}#mermaid-svg-4DhaxVJjJeT6HEtE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4DhaxVJjJeT6HEtE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4DhaxVJjJeT6HEtE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4DhaxVJjJeT6HEtE .marker.cross{stroke:#333333;}#mermaid-svg-4DhaxVJjJeT6HEtE svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4DhaxVJjJeT6HEtE p{margin:0;}#mermaid-svg-4DhaxVJjJeT6HEtE .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-4DhaxVJjJeT6HEtE .cluster-label text{fill:#333;}#mermaid-svg-4DhaxVJjJeT6HEtE .cluster-label span{color:#333;}#mermaid-svg-4DhaxVJjJeT6HEtE .cluster-label span p{background-color:transparent;}#mermaid-svg-4DhaxVJjJeT6HEtE .label text,#mermaid-svg-4DhaxVJjJeT6HEtE span{fill:#333;color:#333;}#mermaid-svg-4DhaxVJjJeT6HEtE .node rect,#mermaid-svg-4DhaxVJjJeT6HEtE .node circle,#mermaid-svg-4DhaxVJjJeT6HEtE .node ellipse,#mermaid-svg-4DhaxVJjJeT6HEtE .node polygon,#mermaid-svg-4DhaxVJjJeT6HEtE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4DhaxVJjJeT6HEtE .rough-node .label text,#mermaid-svg-4DhaxVJjJeT6HEtE .node .label text,#mermaid-svg-4DhaxVJjJeT6HEtE .image-shape .label,#mermaid-svg-4DhaxVJjJeT6HEtE .icon-shape .label{text-anchor:middle;}#mermaid-svg-4DhaxVJjJeT6HEtE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4DhaxVJjJeT6HEtE .rough-node .label,#mermaid-svg-4DhaxVJjJeT6HEtE .node .label,#mermaid-svg-4DhaxVJjJeT6HEtE .image-shape .label,#mermaid-svg-4DhaxVJjJeT6HEtE .icon-shape .label{text-align:center;}#mermaid-svg-4DhaxVJjJeT6HEtE .node.clickable{cursor:pointer;}#mermaid-svg-4DhaxVJjJeT6HEtE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4DhaxVJjJeT6HEtE .arrowheadPath{fill:#333333;}#mermaid-svg-4DhaxVJjJeT6HEtE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4DhaxVJjJeT6HEtE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4DhaxVJjJeT6HEtE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4DhaxVJjJeT6HEtE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4DhaxVJjJeT6HEtE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4DhaxVJjJeT6HEtE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4DhaxVJjJeT6HEtE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4DhaxVJjJeT6HEtE .cluster text{fill:#333;}#mermaid-svg-4DhaxVJjJeT6HEtE .cluster span{color:#333;}#mermaid-svg-4DhaxVJjJeT6HEtE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4DhaxVJjJeT6HEtE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4DhaxVJjJeT6HEtE rect.text{fill:none;stroke-width:0;}#mermaid-svg-4DhaxVJjJeT6HEtE .icon-shape,#mermaid-svg-4DhaxVJjJeT6HEtE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4DhaxVJjJeT6HEtE .icon-shape p,#mermaid-svg-4DhaxVJjJeT6HEtE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4DhaxVJjJeT6HEtE .icon-shape .label rect,#mermaid-svg-4DhaxVJjJeT6HEtE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4DhaxVJjJeT6HEtE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4DhaxVJjJeT6HEtE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4DhaxVJjJeT6HEtE :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    敌方AI系统

    敌方坦克类型

    基础型

    快速型

    强化型

    装甲型

    AI行为决策

    移动决策

    射击决策

    方向决策

    生成系统

    生成点管理

    数量控制

    类型分配

    一、敌方坦克类型设计

    1.1 敌方类型枚举

    在ArkTS中,我们使用枚举来定义不同的敌方坦克类型:

    export enum EnemyType {
    BASIC = 0, // 基础型:普通速度,普通生命
    FAST = 1, // 快速型:高速移动
    POWER = 2, // 强化型:高伤害子弹
    ARMOR = 3 // 装甲型:高生命值
    }

    每种类型都有其独特的属性配置:

    private initializeByType(): void {
    switch (this.enemyType) {
    case EnemyType.BASIC:
    this.speed = 80;
    this.health = 1;
    this.bulletDamage = 1;
    this.color = '#808080';
    break;
    case EnemyType.FAST:
    this.speed = 150;
    this.health = 1;
    this.bulletDamage = 1;
    this.color = '#FFFF00';
    break;
    case EnemyType.POWER:
    this.speed = 80;
    this.health = 1;
    this.bulletDamage = 2;
    this.color = '#00FFFF';
    break;
    case EnemyType.ARMOR:
    this.speed = 60;
    this.health = 3;
    this.bulletDamage = 1;
    this.color = '#FF00FF';
    break;
    }
    }

    图 1 不同颜色代表不同类型坦克
    在这里插入图片描述

    1.2 敌方坦克数据接口

    为了方便数据传输和状态管理,我们定义了敌方坦克数据接口:

    export interface EnemyTankData {
    x: number;
    y: number;
    direction: TankDirection;
    enemyType: EnemyType;
    health: number;
    isAlive: boolean;
    }

    这个接口包含了敌方坦克的所有关键状态信息,可以用于:

    • 网络同步(多人游戏)
    • 状态保存
    • UI显示
    • 调试输出

    二、敌方坦克类实现

    2.1 类结构设计

    敌方坦克类继承自坦克基类,添加了AI特有的属性和行为:

    export class EnemyTank extends TankBase {
    // 敌方类型
    private enemyType: EnemyType = EnemyType.BASIC;

    // AI行为参数
    private changeDirectionTimer: number = 0;
    private changeDirectionInterval: number = 2.0;

    // 射击参数
    private shootCooldown: number = 0;
    private shootInterval: number = 2.0;

    // 移动参数
    private moveTimer: number = 0;

    constructor(x: number, y: number, enemyType: EnemyType = EnemyType.BASIC) {
    super(x, y, TankType.ENEMY);
    this.enemyType = enemyType;
    this.initializeByType();
    }
    }

    #mermaid-svg-UdbahXkRNuO3PMvk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UdbahXkRNuO3PMvk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UdbahXkRNuO3PMvk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UdbahXkRNuO3PMvk .error-icon{fill:#552222;}#mermaid-svg-UdbahXkRNuO3PMvk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UdbahXkRNuO3PMvk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UdbahXkRNuO3PMvk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UdbahXkRNuO3PMvk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UdbahXkRNuO3PMvk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UdbahXkRNuO3PMvk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UdbahXkRNuO3PMvk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UdbahXkRNuO3PMvk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UdbahXkRNuO3PMvk .marker.cross{stroke:#333333;}#mermaid-svg-UdbahXkRNuO3PMvk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UdbahXkRNuO3PMvk p{margin:0;}#mermaid-svg-UdbahXkRNuO3PMvk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-UdbahXkRNuO3PMvk .cluster-label text{fill:#333;}#mermaid-svg-UdbahXkRNuO3PMvk .cluster-label span{color:#333;}#mermaid-svg-UdbahXkRNuO3PMvk .cluster-label span p{background-color:transparent;}#mermaid-svg-UdbahXkRNuO3PMvk .label text,#mermaid-svg-UdbahXkRNuO3PMvk span{fill:#333;color:#333;}#mermaid-svg-UdbahXkRNuO3PMvk .node rect,#mermaid-svg-UdbahXkRNuO3PMvk .node circle,#mermaid-svg-UdbahXkRNuO3PMvk .node ellipse,#mermaid-svg-UdbahXkRNuO3PMvk .node polygon,#mermaid-svg-UdbahXkRNuO3PMvk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UdbahXkRNuO3PMvk .rough-node .label text,#mermaid-svg-UdbahXkRNuO3PMvk .node .label text,#mermaid-svg-UdbahXkRNuO3PMvk .image-shape .label,#mermaid-svg-UdbahXkRNuO3PMvk .icon-shape .label{text-anchor:middle;}#mermaid-svg-UdbahXkRNuO3PMvk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UdbahXkRNuO3PMvk .rough-node .label,#mermaid-svg-UdbahXkRNuO3PMvk .node .label,#mermaid-svg-UdbahXkRNuO3PMvk .image-shape .label,#mermaid-svg-UdbahXkRNuO3PMvk .icon-shape .label{text-align:center;}#mermaid-svg-UdbahXkRNuO3PMvk .node.clickable{cursor:pointer;}#mermaid-svg-UdbahXkRNuO3PMvk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UdbahXkRNuO3PMvk .arrowheadPath{fill:#333333;}#mermaid-svg-UdbahXkRNuO3PMvk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UdbahXkRNuO3PMvk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UdbahXkRNuO3PMvk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UdbahXkRNuO3PMvk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UdbahXkRNuO3PMvk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UdbahXkRNuO3PMvk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UdbahXkRNuO3PMvk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UdbahXkRNuO3PMvk .cluster text{fill:#333;}#mermaid-svg-UdbahXkRNuO3PMvk .cluster span{color:#333;}#mermaid-svg-UdbahXkRNuO3PMvk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UdbahXkRNuO3PMvk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UdbahXkRNuO3PMvk rect.text{fill:none;stroke-width:0;}#mermaid-svg-UdbahXkRNuO3PMvk .icon-shape,#mermaid-svg-UdbahXkRNuO3PMvk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UdbahXkRNuO3PMvk .icon-shape p,#mermaid-svg-UdbahXkRNuO3PMvk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UdbahXkRNuO3PMvk .icon-shape .label rect,#mermaid-svg-UdbahXkRNuO3PMvk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UdbahXkRNuO3PMvk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UdbahXkRNuO3PMvk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UdbahXkRNuO3PMvk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    TankBase基类

    EnemyTank类

    敌方类型属性

    AI行为参数

    射击参数

    移动参数

    enemyType

    changeDirectionTimer

    changeDirectionInterval

    shootCooldown

    shootInterval

    2.2 AI行为决策系统

    敌方坦克的AI行为包括三个主要决策:移动、射击、方向改变。

    2.2.1 移动决策

    update(deltaTime: number): void {
    if (!this.isActive) {
    return;
    }

    // 更新AI行为计时器
    this.changeDirectionTimer += deltaTime;
    this.shootCooldown -= deltaTime;
    this.moveTimer += deltaTime;

    // 方向改变决策
    if (this.changeDirectionTimer >= this.changeDirectionInterval) {
    this.changeDirection();
    this.changeDirectionTimer = 0;
    // 随机化下次改变方向的时间
    this.changeDirectionInterval = 1.0 + Math.random() * 3.0;
    }

    // 执行移动
    super.update(deltaTime);
    }

    2.2.2 射击决策

    canShoot(): boolean {
    return this.isActive && this.shootCooldown <= 0;
    }

    shoot(): void {
    this.shootCooldown = this.shootInterval;
    }

    createBullet(): Bullet | null {
    if (!this.canShoot()) {
    return null;
    }

    this.shoot();

    const center = this.getCenter();
    let bulletX = center.x – 4;
    let bulletY = center.y – 4;

    return new Bullet(bulletX, bulletY, this.direction, this.bulletDamage, false, 0);
    }

    2.2.3 方向决策

    changeDirection(): void {
    // 随机选择新方向
    const directions = [
    TankDirection.UP,
    TankDirection.DOWN,
    TankDirection.LEFT,
    TankDirection.RIGHT
    ];

    // 过滤掉当前方向,避免连续选择相同方向
    const availableDirections = directions.filter(d => d !== this.direction);

    // 随机选择
    const randomIndex = Math.floor(Math.random() * availableDirections.length);
    this.direction = availableDirections[randomIndex];
    }

    2.3 敌方坦克销毁

    destroy(): void {
    this.isActive = false;
    this.health = 0;
    }

    takeDamage(damage: number): void {
    this.health -= damage;
    if (this.health <= 0) {
    this.destroy();
    }
    }

    三、敌方生成系统

    3.1 生成器类设计

    敌方生成器负责管理敌方的生成时机、位置和数量:

    export class EnemySpawner {
    // 生成点位置
    private spawnPoints: SpawnPoint[] = [];

    // 当前关卡
    private currentLevel: number = 1;

    // 剩余敌方坦克数量
    private remainingEnemies: number = 0;

    // 最大同时存在的敌方坦克数量
    private maxActiveEnemies: number = 4;

    // 生成冷却时间
    private spawnCooldown: number = 0;
    private spawnInterval: number = 3.0;

    // 敌方类型分配
    private enemyTypeDistribution: EnemyType[] = [];
    }

    3.2 生成点初始化

    initializeSpawnPoints(mapWidth: number): void {
    this.spawnPoints = [];

    // 在地图顶部设置三个生成点
    const spacing = mapWidth / 4;

    this.spawnPoints.push({ x: spacing, y: 0 });
    this.spawnPoints.push({ x: spacing * 2, y: 0 });
    this.spawnPoints.push({ x: spacing * 3, y: 0 });

    // 打乱生成点顺序,增加随机性
    this.shuffleArray(this.spawnPoints);
    }

    3.3 敌方类型分配

    根据关卡难度,动态调整敌方类型分配:

    initializeEnemyTypes(): void {
    this.enemyTypeDistribution = [];

    // 根据关卡调整类型分布
    const basicCount = Math.max(10 – this.currentLevel, 3);
    const fastCount = Math.min(this.currentLevel, 5);
    const powerCount = Math.max(this.currentLevel – 3, 0);
    const armorCount = Math.max(this.currentLevel – 5, 0);

    // 添加敌方类型到分配数组
    for (let i = 0; i < basicCount; i++) {
    this.enemyTypeDistribution.push(EnemyType.BASIC);
    }
    for (let i = 0; i < fastCount; i++) {
    this.enemyTypeDistribution.push(EnemyType.FAST);
    }
    for (let i = 0; i < powerCount; i++) {
    this.enemyTypeDistribution.push(EnemyType.POWER);
    }
    for (let i = 0; i < armorCount; i++) {
    this.enemyTypeDistribution.push(EnemyType.ARMOR);
    }

    // 打乱顺序
    this.shuffleArray(this.enemyTypeDistribution);
    }

    #mermaid-svg-oLJYOL8W57x4ywgD{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oLJYOL8W57x4ywgD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oLJYOL8W57x4ywgD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oLJYOL8W57x4ywgD .error-icon{fill:#552222;}#mermaid-svg-oLJYOL8W57x4ywgD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oLJYOL8W57x4ywgD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oLJYOL8W57x4ywgD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oLJYOL8W57x4ywgD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oLJYOL8W57x4ywgD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oLJYOL8W57x4ywgD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oLJYOL8W57x4ywgD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oLJYOL8W57x4ywgD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oLJYOL8W57x4ywgD .marker.cross{stroke:#333333;}#mermaid-svg-oLJYOL8W57x4ywgD svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oLJYOL8W57x4ywgD p{margin:0;}#mermaid-svg-oLJYOL8W57x4ywgD .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-oLJYOL8W57x4ywgD .cluster-label text{fill:#333;}#mermaid-svg-oLJYOL8W57x4ywgD .cluster-label span{color:#333;}#mermaid-svg-oLJYOL8W57x4ywgD .cluster-label span p{background-color:transparent;}#mermaid-svg-oLJYOL8W57x4ywgD .label text,#mermaid-svg-oLJYOL8W57x4ywgD span{fill:#333;color:#333;}#mermaid-svg-oLJYOL8W57x4ywgD .node rect,#mermaid-svg-oLJYOL8W57x4ywgD .node circle,#mermaid-svg-oLJYOL8W57x4ywgD .node ellipse,#mermaid-svg-oLJYOL8W57x4ywgD .node polygon,#mermaid-svg-oLJYOL8W57x4ywgD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oLJYOL8W57x4ywgD .rough-node .label text,#mermaid-svg-oLJYOL8W57x4ywgD .node .label text,#mermaid-svg-oLJYOL8W57x4ywgD .image-shape .label,#mermaid-svg-oLJYOL8W57x4ywgD .icon-shape .label{text-anchor:middle;}#mermaid-svg-oLJYOL8W57x4ywgD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oLJYOL8W57x4ywgD .rough-node .label,#mermaid-svg-oLJYOL8W57x4ywgD .node .label,#mermaid-svg-oLJYOL8W57x4ywgD .image-shape .label,#mermaid-svg-oLJYOL8W57x4ywgD .icon-shape .label{text-align:center;}#mermaid-svg-oLJYOL8W57x4ywgD .node.clickable{cursor:pointer;}#mermaid-svg-oLJYOL8W57x4ywgD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oLJYOL8W57x4ywgD .arrowheadPath{fill:#333333;}#mermaid-svg-oLJYOL8W57x4ywgD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oLJYOL8W57x4ywgD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oLJYOL8W57x4ywgD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oLJYOL8W57x4ywgD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oLJYOL8W57x4ywgD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oLJYOL8W57x4ywgD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oLJYOL8W57x4ywgD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oLJYOL8W57x4ywgD .cluster text{fill:#333;}#mermaid-svg-oLJYOL8W57x4ywgD .cluster span{color:#333;}#mermaid-svg-oLJYOL8W57x4ywgD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oLJYOL8W57x4ywgD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oLJYOL8W57x4ywgD rect.text{fill:none;stroke-width:0;}#mermaid-svg-oLJYOL8W57x4ywgD .icon-shape,#mermaid-svg-oLJYOL8W57x4ywgD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oLJYOL8W57x4ywgD .icon-shape p,#mermaid-svg-oLJYOL8W57x4ywgD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oLJYOL8W57x4ywgD .icon-shape .label rect,#mermaid-svg-oLJYOL8W57x4ywgD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oLJYOL8W57x4ywgD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oLJYOL8W57x4ywgD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oLJYOL8W57x4ywgD :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    关卡开始

    初始化生成点

    初始化敌方类型

    计算基础型数量

    计算快速型数量

    计算强化型数量

    计算装甲型数量

    生成类型数组

    打乱数组顺序

    准备生成

    3.4 生成逻辑

    update(deltaTime: number, activeEnemyCount: number): EnemyTank | null {
    // 更新冷却时间
    if (this.spawnCooldown > 0) {
    this.spawnCooldown -= deltaTime;
    }

    // 检查是否可以生成
    if (this.canSpawn(activeEnemyCount)) {
    return this.spawnEnemy();
    }

    return null;
    }

    private canSpawn(activeEnemyCount: number): boolean {
    return this.remainingEnemies > 0 &&
    activeEnemyCount < this.maxActiveEnemies &&
    this.spawnCooldown <= 0 &&
    this.enemyTypeDistribution.length > 0;
    }

    private spawnEnemy(): EnemyTank {
    // 选择生成点
    const spawnPoint = this.spawnPoints[this.currentSpawnIndex];
    this.currentSpawnIndex = (this.currentSpawnIndex + 1) % this.spawnPoints.length;

    // 选择敌方类型
    const enemyType = this.enemyTypeDistribution.pop()!;

    // 创建敌方坦克
    const enemy = new EnemyTank(
    spawnPoint.x * 32,
    spawnPoint.y * 32,
    enemyType
    );

    // 更新状态
    this.remainingEnemies—;
    this.spawnCooldown = this.spawnInterval;

    return enemy;
    }

    四、AI行为优化

    4.1 智能方向选择

    简单的随机方向选择可能导致敌方坦克频繁撞墙,我们可以优化方向选择逻辑:

    changeDirection(): void {
    // 优先选择朝向玩家基地的方向
    const baseX = 13; // 基地X坐标
    const baseY = 25; // 基地Y坐标

    const dx = baseX – this.x;
    const dy = baseY – this.y;

    // 根据距离选择方向
    let preferredDirection: TankDirection;
    if (Math.abs(dx) > Math.abs(dy)) {
    preferredDirection = dx > 0 ? TankDirection.RIGHT : TankDirection.LEFT;
    } else {
    preferredDirection = dy > 0 ? TankDirection.DOWN : TankDirection.UP;
    }

    // 70%概率选择优先方向,30%概率随机选择
    if (Math.random() < 0.7) {
    this.direction = preferredDirection;
    } else {
    // 随机选择
    const directions = [
    TankDirection.UP,
    TankDirection.DOWN,
    TankDirection.LEFT,
    TankDirection.RIGHT
    ];
    const randomIndex = Math.floor(Math.random() * directions.length);
    this.direction = directions[randomIndex];
    }
    }

    4.2 射击时机优化

    canShoot(): boolean {
    // 基础检查
    if (!this.isActive || this.shootCooldown > 0) {
    return false;
    }

    // 增加射击概率,避免过于频繁
    return Math.random() < 0.3;
    }

    4.3 难度动态调整

    根据玩家表现动态调整AI难度:

    adjustDifficulty(playerScore: number, playerLives: number): void {
    // 玩家分数越高,AI越积极
    if (playerScore > 5000) {
    this.shootInterval = 1.5;
    this.changeDirectionInterval = 1.0;
    } else if (playerScore > 10000) {
    this.shootInterval = 1.0;
    this.changeDirectionInterval = 0.5;
    }

    // 玩家生命越少,AI越消极
    if (playerLives <= 1) {
    this.shootInterval *= 1.5;
    this.changeDirectionInterval *= 1.5;
    }
    }

    五、实战应用

    5.1 在游戏主循环中使用

    // 在GamePage中
    private enemySpawner: EnemySpawner = new EnemySpawner();
    private enemyTanks: EnemyTank[] = [];

    // 初始化
    aboutToAppear(): void {
    this.enemySpawner.initializeSpawnPoints(this.mapManager.getMapWidth());
    this.enemySpawner.setTotalEnemies(20);
    }

    // 更新
    update(deltaTime: number): void {
    // 更新生成器
    const newEnemy = this.enemySpawner.update(deltaTime, this.enemyTanks.length);
    if (newEnemy) {
    this.enemyTanks.push(newEnemy);
    }

    // 更新所有敌方坦克
    for (const enemy of this.enemyTanks) {
    enemy.update(deltaTime);

    // AI射击
    const bullet = enemy.createBullet();
    if (bullet) {
    this.bullets.push(bullet);
    }
    }
    }

    5.2 性能优化

    // 使用对象池管理敌方坦克
    private enemyPool: EnemyTank[] = [];

    getEnemyFromPool(x: number, y: number, type: EnemyType): EnemyTank {
    if (this.enemyPool.length > 0) {
    const enemy = this.enemyPool.pop()!;
    enemy.reset(x, y, type);
    return enemy;
    }
    return new EnemyTank(x, y, type);
    }

    returnEnemyToPool(enemy: EnemyTank): void {
    enemy.isActive = false;
    this.enemyPool.push(enemy);
    }

    六、调试与测试

    6.1 可视化调试

    // 绘制敌方AI状态
    drawEnemyAIState(canvas: CanvasRenderingContext2D, enemy: EnemyTank): void {
    // 绘制方向指示
    const center = enemy.getCenter();
    canvas.strokeStyle = '#FF0000';
    canvas.lineWidth = 2;
    canvas.beginPath();
    canvas.moveTo(center.x, center.y);

    switch (enemy.direction) {
    case TankDirection.UP:
    canvas.lineTo(center.x, center.y – 20);
    break;
    case TankDirection.DOWN:
    canvas.lineTo(center.x, center.y + 20);
    break;
    case TankDirection.LEFT:
    canvas.lineTo(center.x – 20, center.y);
    break;
    case TankDirection.RIGHT:
    canvas.lineTo(center.x + 20, center.y);
    break;
    }
    canvas.stroke();
    }

    6.2 单元测试

    // 测试敌方生成
    testEnemySpawn(): void {
    const spawner = new EnemySpawner();
    spawner.initializeSpawnPoints(26);
    spawner.setTotalEnemies(10);

    // 测试生成
    const enemy = spawner.update(0, 0);
    console.assert(enemy !== null, '敌方应该生成');
    console.assert(enemy!.isActive, '敌方应该激活');
    }

    // 测试AI行为
    testEnemyAI(): void {
    const enemy = new EnemyTank(100, 100, EnemyType.BASIC);

    // 测试方向改变
    const oldDirection = enemy.direction;
    enemy.changeDirection();
    console.assert(enemy.direction !== oldDirection, '方向应该改变');

    // 测试射击
    const bullet = enemy.createBullet();
    console.assert(bullet !== null, '子弹应该创建');
    }

    七、总结

    7.1 核心设计要点

  • 类型多样化:四种敌方类型提供不同挑战
  • AI行为智能化:移动、射击、方向决策系统
  • 生成系统可控:数量、时机、位置精确控制
  • 难度动态化:根据玩家表现调整AI难度
  • 7.2 性能优化建议

  • 对象池技术:减少对象创建销毁开销
  • 状态缓存:避免重复计算
  • 批量处理:减少循环次数
  • 延迟加载:按需加载AI资源
  • 7.3 扩展方向

  • 路径规划:使用A*算法实现智能寻路
  • 团队协作:多个敌方协同作战
  • 战术系统:包围、伏击等战术行为
  • 学习系统:基于玩家行为的机器学习
  • 系列文章导航:

    下期预告: 坦克大战游戏开发实战(八)-道具系统架构设计


    赞(0)
    未经允许不得转载:171主机测评 » 坦克大战游戏开发实战(七)-敌方AI系统设计与实现
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址