当前位置: 首页 > news >正文

从零开发游戏需要学习的c#模块,第二十六章(多种敌人与基础 AI)

本节课目标

  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. 在UpdateGame状态下更新敌人 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);

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

http://www.jsqmd.com/news/899872/

相关文章:

  • TVA现阶段快速进入的五大核心应用场景
  • 2025-2026年发动机缸盖工厂推荐:十大排行专业评测加工精度案例价格 - 品牌推荐
  • 保姆级教程:用ROS的navigation和move_base让小车自己跑起来(附避坑指南)
  • 5G网络基石:从APN到DNN的演进与核心配置解析
  • 异构加速器上并行FFT算法设计与性能优化实践
  • (良心整理)亲测靠谱的AI论文网站,毕业党收藏备用
  • 远程控制哪家稳?地铁高铁酒店WiFi实测,ToDesk弱网优化最强
  • 学术写作效率突破!2026全能型AI论文软件精选指南
  • AI智能体视觉开启人工智能时代新纪元
  • Unity手游开发:用Joystick Pack插件5分钟搞定虚拟摇杆,适配移动端触屏操作
  • HETI架构与堆叠寄存器文件:硬件加速中断上下文切换的嵌入式实时系统优化
  • 从零开发游戏需要学习的c#模块,第二十七章(远程攻击 —— 发射子弹)
  • 【仅限首批500家企业获取】ChatGPT客服话术智能诊断工具包(含话术熵值分析器+合规风险热力图+客户情绪拐点预测模型)
  • 量子网络全栈协同设计:从异构互联到可扩展架构的工程实践
  • 2025-2026年发动机缸盖工厂推荐:五大排行产品专业评测自动化产线防气孔缺陷注意事项 - 品牌推荐
  • 从一次偶发性RST探秘TCP协议栈与NAT的隐秘冲突
  • 智能制造的关键入口:从传统视觉到AI智能体视觉(系列)
  • 第一篇:为什么多个 Flow collect 必须 launch?——一篇讲透 Android 协程生命周期
  • SRT除法器性能优化:Skip-Zero策略的原理、实现与Chisel实践
  • 迭代扰动粒子滤波:突破重采样瓶颈,实现并行化贝叶斯状态估计
  • AIBOX-1684X系统固件升级入门教程
  • ChatGPT产品描述生成失效真相(90%团队踩中的5个认知陷阱)
  • 哪家发动机缸盖工厂专业?2026年5月推荐TOP5对比砂眼控制评测适用场景特点 - 品牌推荐
  • 2026年南宁钢塑管供应市场深度解析:聚焦广西水之龙建材有限公司 - 2026年企业资讯
  • 如何用Python命令行工具突破百度网盘下载限速:完整实战指南
  • 高光谱与农业(一)从叶片光谱到作物表型:漫反射的测量挑战与早期探索
  • ngx_http_request_finalizer
  • 移动端开发:React Native跨平台实战
  • Azure云服务智能工具与数据库定价优化实战指南
  • 2026年5月AGV叉车厂家推荐:十大排名专业评测性价比高价格注意事项 - 品牌推荐