本节课目标创建三种敌人史莱姆慢、骷髅中速追人、蝙蝠快速随机移动敌人有独立的行为逻辑不同敌人有不同外观和属性敌人在地图上自主移动第一步创建敌人基类右键项目 →添加→类文件名Enemy.cscsharpusing 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 ListVector2 enemies default!;// 新的private ListEnemy 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);本节课学习任务到此结束我是魔法阵维护师关注我下期更精彩
从零开发游戏需要学习的c#模块,第二十六章(多种敌人与基础 AI)
发布时间:2026/5/28 0:22:14
本节课目标创建三种敌人史莱姆慢、骷髅中速追人、蝙蝠快速随机移动敌人有独立的行为逻辑不同敌人有不同外观和属性敌人在地图上自主移动第一步创建敌人基类右键项目 →添加→类文件名Enemy.cscsharpusing 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 ListVector2 enemies default!;// 新的private ListEnemy 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);本节课学习任务到此结束我是魔法阵维护师关注我下期更精彩