本节课目标

  1. 创建三种敌人:史莱姆(慢)、骷髅(中速追人)、蝙蝠(快速随机移动)

  2. 敌人有独立的行为逻辑

  3. 不同敌人有不同外观和属性

  4. 敌人在地图上自主移动


第一步:创建敌人基类

右键项目 → 添加 → ,文件名 Enemy.cs

csharp

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;

namespace MY_FIRST_GAME
{
    public enum EnemyType
    {
        Slime,    // 史莱姆:慢速随机移动
        Skeleton, // 骷髅:追踪玩家
        Bat       // 蝙蝠:快速随机移动
    }

    public class Enemy
    {
        public Vector2 Position;
        public EnemyType Type;
        public int Hp;
        public int MaxHp;
        public int Attack;
        public int ScoreValue;
        public float Speed;
        public bool IsAlive;

        protected Texture2D texture;
        protected Color color;
        protected Random rng;
        protected float changeTimer;
        protected float changeInterval;
        protected Vector2 moveDirection;

        public Enemy(EnemyType type, Vector2 position, GraphicsDevice graphicsDevice)
        {
            Type = type;
            Position = position;
            IsAlive = true;
            rng = new Random();
            changeTimer = 0f;
            changeInterval = (float)(rng.NextDouble() * 2 + 1);

            // 根据类型设置属性
            switch (type)
            {
                case EnemyType.Slime:
                    Hp = 30; MaxHp = 30; Attack = 8;
                    ScoreValue = 30; Speed = 40f;
                    color = Color.Green;
                    break;
                case EnemyType.Skeleton:
                    Hp = 60; MaxHp = 60; Attack = 15;
                    ScoreValue = 80; Speed = 80f;
                    color = Color.White;
                    break;
                case EnemyType.Bat:
                    Hp = 15; MaxHp = 15; Attack = 5;
                    ScoreValue = 20; Speed = 150f;
                    color = Color.Purple;
                    break;
            }

            // 创建纹理
            int size = (type == EnemyType.Bat) ? 24 : 40;
            texture = new Texture2D(graphicsDevice, size, size);
            Color[] data = new Color[size * size];

            if (type == EnemyType.Bat)
            {
                // 蝙蝠是三角形
                for (int y = 0; y < size; y++)
                    for (int x = 0; x < size; x++)
                    {
                        if (Math.Abs(x - size / 2) < y * 0.8f && y < size * 0.7f)
                            data[y * size + x] = color;
                        else
                            data[y * size + x] = Color.Transparent;
                    }
            }
            else if (type == EnemyType.Skeleton)
            {
                // 骷髅是菱形
                for (int y = 0; y < size; y++)
                    for (int x = 0; x < size; x++)
                    {
                        float dx = Math.Abs(x - size / 2f);
                        float dy = Math.Abs(y - size / 2f);
                        if (dx + dy < size / 2f)
                            data[y * size + x] = color;
                        else
                            data[y * size + x] = Color.Transparent;
                    }
            }
            else
            {
                // 史莱姆是圆形
                for (int y = 0; y < size; y++)
                    for (int x = 0; x < size; x++)
                    {
                        float dx = x - size / 2f;
                        float dy = y - size / 2f;
                        if (dx * dx + dy * dy < (size / 2f) * (size / 2f))
                            data[y * size + x] = color;
                        else
                            data[y * size + x] = Color.Transparent;
                    }
            }
            texture.SetData(data);

            // 初始随机方向
            float angle = (float)(rng.NextDouble() * Math.PI * 2);
            moveDirection = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
        }

        public virtual void Update(float deltaTime, Vector2 playerPosition, TileMap tileMap)
        {
            changeTimer += deltaTime;

            switch (Type)
            {
                case EnemyType.Slime:
                    UpdateSlime(deltaTime, tileMap);
                    break;
                case EnemyType.Skeleton:
                    UpdateSkeleton(deltaTime, playerPosition, tileMap);
                    break;
                case EnemyType.Bat:
                    UpdateBat(deltaTime, tileMap);
                    break;
            }
        }

        // ★ 史莱姆:慢速随机移动
        private void UpdateSlime(float deltaTime, TileMap tileMap)
        {
            if (changeTimer >= changeInterval)
            {
                changeTimer = 0f;
                changeInterval = (float)(rng.NextDouble() * 3 + 1);
                float angle = (float)(rng.NextDouble() * Math.PI * 2);
                moveDirection = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
            }

            Vector2 newPos = Position + moveDirection * Speed * deltaTime;
            if (!tileMap.IsWall(newPos))
                Position = newPos;
            else
                changeTimer = changeInterval; // 撞墙就换方向
        }

        // ★ 骷髅:追踪玩家
        private void UpdateSkeleton(float deltaTime, Vector2 playerPosition, TileMap tileMap)
        {
            Vector2 toPlayer = playerPosition - Position;
            if (toPlayer.Length() > 0)
            {
                moveDirection = Vector2.Normalize(toPlayer);
            }

            Vector2 newPos = Position + moveDirection * Speed * deltaTime;
            if (!tileMap.IsWall(newPos))
                Position = newPos;
        }

        // ★ 蝙蝠:快速随机移动,频繁换向
        private void UpdateBat(float deltaTime, TileMap tileMap)
        {
            if (changeTimer >= changeInterval)
            {
                changeTimer = 0f;
                changeInterval = (float)(rng.NextDouble() * 0.5 + 0.3);
                float angle = (float)(rng.NextDouble() * Math.PI * 2);
                moveDirection = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
            }

            Vector2 newPos = Position + moveDirection * Speed * deltaTime;
            if (!tileMap.IsWall(newPos))
                Position = newPos;
            else
                changeTimer = changeInterval;
        }

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(texture, Position, null, Color.White,
                0f, new Vector2(texture.Width / 2, texture.Height / 2),
                1f, SpriteEffects.None, 0f);
        }

        public Rectangle GetBounds()
        {
            return new Rectangle(
                (int)(Position.X - texture.Width / 2),
                (int)(Position.Y - texture.Height / 2),
                texture.Width, texture.Height);
        }
    }
}

第二步:改造 Game1.cs

以下是需要修改的部分。为了节省篇幅,我只列出改动的地方,你按位置替换即可。

1. 替换敌人列表类型:

// 旧的
private List<Vector2> enemies = default!;

// 新的
private List<Enemy> enemies = default!;

2. 替换 SpawnEnemies 方法:

private void SpawnEnemies(int count)
{
    for (int i = 0; i < count; i++)
    {
        Vector2 pos = tileMap.GetRandomEmptyPosition(rng);
        EnemyType type = (EnemyType)(rng.Next(3)); // 随机类型
        enemies.Add(new Enemy(type, pos, GraphicsDevice));
    }
}

3. 在 Update 的 Game 状态下更新敌人 AI:

// 在 CheckEnemyCollision 之前加
foreach (Enemy enemy in enemies)
    enemy.Update(deltaTime, player.Position, tileMap);

4. 替换 CheckEnemyCollision 方法:

private void CheckEnemyCollision()
{
    Rectangle playerRect = player.GetBounds();
    for (int i = enemies.Count - 1; i >= 0; i--)
    {
        if (enemies[i].GetBounds().Intersects(playerRect))
        {
            Enemy enemy = enemies[i];
            enemies.RemoveAt(i);
            score += enemy.ScoreValue;
            enemiesDefeatedThisGame++;
            player.Hp -= enemy.Attack;
            particleSystem.EmitHitParticles(enemy.Position);
            try { hitSound?.Play(); } catch { }
        }
    }
}

5. 替换 DrawGameWorld 里的敌人绘制:

foreach (Enemy enemy in enemies)
    enemy.Draw(_spriteBatch);

本节课学习任务到此结束,我是魔法阵维护师,关注我,下期更精彩!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐