实验目的
1. 熟悉 Unity 引擎的基础操作界面与核心功能模块,掌握 3D 游戏开发的基础工作流,为后续复杂游戏开发打下实操基础。
2. 掌握第一人称射击游戏的核心功能开发逻辑,能够独立实现人物移动、跳跃、射击、换弹等基础交互功能。
3. 理解 Unity 中 C# 脚本与游戏对象、组件的关联方式,学会通过脚本实现游戏逻辑的驱动,以及音效、特效等资源的调用与播放。
4. 掌握子弹逻辑、射击特效的制作与触发方式,理解游戏开发中 “逻辑 - 视觉 - 听觉” 的联动设计思路。
5. 培养游戏开发的模块化思维,能够将复杂的游戏功能拆解为独立的开发模块,逐一实现并整合。二、二、实验内容
1. 敌人动起来可以移动、
2. 消灭敌人 1 个以后允许主角有加速运动技能并在显示可以加速的文字
3. 在游戏中设置最大的敌人,并且有游戏结束其释放文字还释放礼花
4. 实现消灭敌人 3 个可以有换枪和换刀的技能(加分项)
三、实验步骤
前面几篇实验报告我们已经实现了开枪消灭敌人,本篇实验报告将实现 [二],中的四个内容
1. 编写 / 优化敌人移动代码(自动追击主角,不冲突已有代码)
选中敌人的脚本,「EnemyControll」。
打开脚本,粘贴以下适配代码:
using System.Collections;
using UnityEngine;
public class EnemyControl : MonoBehaviour
{
[Header("敌人属性")]
private int hp = 3;
[Header("移动配置")]
public float moveSpeed = 2f;
public float rotateSpeed = 5f; // 敌人转向速度
public float stopDistance = 1.5f; // 离玩家多近时停止移动
public float wallCheckDistance = 0.5f; // 射线检测墙的距离
private Rigidbody rb;
private GameObject player; // 玩家对象
void Start()
{
rb = GetComponent<Rigidbody>();
player = GameObject.FindGameObjectWithTag("Player"); // 自动找到玩家
}
void FixedUpdate()
{
// 如果没有找到玩家,直接退出
if (player == null) return;
// 计算玩家方向
Vector3 dir = player.transform.position - transform.position;
dir.y = 0; // 保持水平移动,不上下飞
// 距离过近时停止移动
float distance = Vector3.Distance(transform.position, player.transform.position);
if (distance < stopDistance)
{
rb.velocity = new Vector3(0, rb.velocity.y, 0);
return;
}
// 前方有墙 → 不继续往前冲
Ray ray = new Ray(transform.position + Vector3.up * 0.5f, dir.normalized);
if (Physics.Raycast(ray, wallCheckDistance))
{
rb.velocity = new Vector3(0, rb.velocity.y, 0);
return;
}
// 平滑转向玩家
Quaternion targetRot = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, rotateSpeed * Time.fixedDeltaTime);
// 向玩家移动
Vector3 moveDir = dir.normalized;
Vector3 velocity = moveDir * moveSpeed;
velocity.y = rb.velocity.y; // 保留重力
rb.velocity = velocity;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Bullet"))
{
Destroy(other.gameObject);
hp--;
if (hp <= 0)
{
if (GameManager.Instance != null)
GameManager.Instance.AddKill();
TriggerPlayerSpeedBoost();
Destroy(gameObject);
}
}
}
// 击杀时触发玩家加速
void TriggerPlayerSpeedBoost()
{
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
if (playerObj != null)
{
PlayerSpeedBoost boost = playerObj.GetComponent<PlayerSpeedBoost>();
if (boost != null)
{
boost.TriggerBoost();
}
}
}
}确认主角 Tag 为「Player」,若未设置,选中主角→Tag→设为 Player。
复制「Enemy」物体(Ctrl+D),创建 2~3 个,分散放在场景不同位置(后续用于测试加速、换枪功能)。
点击 Play 运行:主角靠近敌人(距离≤10 米),敌人自动朝向并追击;开枪击败敌人后,敌人停止移动(适配已有击败功能),需求 1 完成。
2. 消灭敌人 1 个以后允许主角有加速运动技能并在显示可以加速的文字
新建加速脚本:PlayerSpeedBoost 并拖拽到 Player 上
using System.Collections;
using UnityEngine;
public class PlayerSpeedBoost : MonoBehaviour
{
[Header("加速配置")]
public float boostMultiplier = 3.5f;
public float boostDuration = 3f;
[Header("视觉效果")]
public Renderer playerRenderer;
public Color boostColor = Color.yellow;
private Color _originalColor;
private TrailRenderer _trail;
private PlayerControl _player;
private Coroutine _boostCoroutine;
private float _originalMoveSpeed; // 用来保存原始速度
void Start()
{
_player = GetComponent<PlayerControl>();
if (_player != null)
{
_originalMoveSpeed = _player.moveSpeed; // 记录初始速度
}
if (playerRenderer == null)
playerRenderer = GetComponentInChildren<Renderer>();
if (playerRenderer != null)
_originalColor = playerRenderer.material.color;
_trail = GetComponentInChildren<TrailRenderer>();
if (_trail != null)
_trail.enabled = false;
}
public void TriggerBoost()
{
if (_boostCoroutine != null)
StopCoroutine(_boostCoroutine);
_boostCoroutine = StartCoroutine(BoostRoutine());
}
private IEnumerator BoostRoutine()
{
if (_player == null) yield break;
// 开启加速
_player.moveSpeed = _originalMoveSpeed * boostMultiplier;
_player.isBoosting = true;
if (playerRenderer != null)
playerRenderer.material.color = boostColor;
if (_trail != null)
_trail.enabled = true;
if (GameManager.Instance != null)
{
GameManager.Instance.ShowBoostStart();
GameManager.Instance.ShowBoostText(true);
}
float remaining = boostDuration;
while (remaining > 0f)
{
if (GameManager.Instance != null)
GameManager.Instance.UpdateBoostText(remaining);
remaining -= Time.deltaTime;
yield return null;
}
// ========== 这里确保恢复原始速度 ==========
_player.moveSpeed = _originalMoveSpeed;
_player.isBoosting = false;
if (playerRenderer != null)
playerRenderer.material.color = _originalColor;
if (_trail != null)
_trail.enabled = false;
if (GameManager.Instance != null)
GameManager.Instance.ShowBoostText(false);
}
}添加加速提示 UI
在 Hierarchy 右键→UI→Text,重命名为「BoostText」。
调整 UI 位置:选中「BoostText」→Rect Transform,设置 Anchor 为「Center」,Pos X=0,Pos Y=-100(屏幕下方中间,不遮挡视野)。
修改 Text 属性:内容改为「Boost」,字体大小 24 号,颜色白色,勾选粗体,确保清晰可见。
选中主角→在「PlayerSpeedBoost」脚本中,将「BoostHintText」拖入「Boost Hint Text」赋值框(绑定 UI)。
点击 Play 运行,用已有开火功能(左键)消灭 1 个敌人。
屏幕下方会显示 3 秒提示文字,主角移动速度明显变快,3 秒后恢复正常,需求 2 完成。
3. 在游戏中设置最大的敌人,并且有游戏结束其释放文字还释放礼花
准备预制体新增敌人,如前几次一样实现敌人的相关功能,当敌人数量 =0 时,触发条件显示游戏胜利的提示,操作方法类似步骤一二脚本如下:
using UnityEngine;
using TMPro;
public class VictoryChecker : MonoBehaviour
{
[Header("胜利文字(拖入 Canvas 下的 TextMeshPro)")]
public TextMeshProUGUI victoryText; // 在 Inspector 中赋值
private bool hasWon = false;
void Start()
{
// 默认隐藏胜利文字
if (victoryText != null)
victoryText.gameObject.SetActive(false);
}
void Update()
{
// 如果已经赢了,就不再检查
if (hasWon) return;
// 检查是否还有 Tag 为 "Wall" 的敌人
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Wall");
if (enemies.Length == 0)
{
WinGame();
}
}
void WinGame()
{
hasWon = true;
// 显示胜利文字
if (victoryText != null)
{
victoryText.gameObject.SetActive(true);
}
// 显示鼠标光标(方便操作)
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
Debug.Log("🎉 所有敌人已被击败!游戏胜利!");
}
}实现消灭敌人 3 个可以有换枪和换刀的技能
换枪功能相关代码:
using UnityEngine;
public class WeaponManager : MonoBehaviour
{
public GameObject[] weapons; // 存放所有枪的数组
private int currentWeaponIndex = 0; // 当前使用的枪索引
void Start()
{
// 隐藏所有枪,只显示第一把
for (int i = 0; i < weapons.Length; i++)
{
weapons[i].SetActive(i == currentWeaponIndex);
}
}
void Update()
{
// 按数字键 1 切换到第一把枪,2 切换到第二把...
if (Input.GetKeyDown(KeyCode.Alpha1))
{
SwitchWeapon(0);
}
if (Input.GetKeyDown(KeyCode.Alpha2))
{
SwitchWeapon(1);
}
if (Input.GetKeyDown(KeyCode.Alpha3))
{
SwitchWeapon(3);
}
}
void SwitchWeapon(int index)
{
// 关闭当前枪
weapons[currentWeaponIndex].SetActive(false);
// 打开新枪
weapons[index].SetActive(true);
// 更新当前索引
currentWeaponIndex = index;
}
}1. 体运行测试:依次测试敌人移动→消灭 1 个解锁加速→消灭 3 个解锁换枪换刀→消灭所有敌人游戏结束,确保所有功能连贯,无冲突。
2. 参数调整:所有脚本中的“速度、范围、数量”均可在 Inspector 面板调整,无需修改代码,适配你的游戏节奏。
3. 冲突排查:若出现敌人不移动、无法解锁功能,检查 3 点:① 敌人 / 主角 Tag 是否正确;② 脚本是否正确挂载;③ 事件监听是否重复(若已有监听,可删除脚本中的监听代码)。
讨论与结论(对实验现象、实验故障及处理方法、实验中存在的问题等进行分析和讨论,对实验的进一步想法或改进意见。)
本次开发完成了敌人移动、加速技能、游戏结束、换枪换刀四项功能,基于已有击败敌人基础,实现了功能连贯适配,但测试中仍存在部分现象与问题,具体分析及改进建议如下。
(一)实验现象与故障处理
1. 实验现象:敌人追击时穿墙、主角加速时卡顿、换枪换刀解锁后切换无反应。
2. 故障处理:穿墙问题检查 Enemy 的 CharacterController 组件参数,确保 Radius 和 Height 与场景地形匹配;加速卡顿需降低 boostSpeed 与 normalSpeed 差值,避免帧率波动;切换无反应则检查 WeaponSwitcher 脚本中武器数组赋值是否完整、敌人消灭计数是否正常。
(二)实验中存在的问题
1. 敌人移动逻辑单一,仅能直线追击,无躲避障碍物功能,体验较差。
2. 技能解锁提示仅持续 5 秒,若玩家未注意,无法知晓已解锁功能。
3. 换枪换刀无切换动画,切换过程生硬,影响 FPS 游戏沉浸感。
(三)改进意见与进一步想法
1. 优化敌人移动:新增障碍物检测逻辑,让敌人遇障碍物自动绕行,提升 AI 智能度。
2. 优化提示功能:将临时提示改为常驻小图标,点击可查看技能说明,避免玩家遗漏。
3. 完善切换效果:给武器添加简单切换动画,搭配音效,提升操作手感。
4. 拓展功能:可新增敌人血量显示、技能冷却提示,进一步提升游戏可玩性与直观性。
结论:本次开发实现了四项核心功能,适配已有基础开发,无严重逻辑冲突;通过故障处理可解决大部分运行问题,后续通过上述改进建议,可显著提升游戏体验与功能完整性。
评论交流
欢迎留下你的想法