1. 实验目的

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("🎉 所有敌人已被击败!游戏胜利!");

    }

}
  1. 实现消灭敌人 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. 拓展功能:可新增敌人血量显示、技能冷却提示,进一步提升游戏可玩性与直观性。

结论:本次开发实现了四项核心功能,适配已有基础开发,无严重逻辑冲突;通过故障处理可解决大部分运行问题,后续通过上述改进建议,可显著提升游戏体验与功能完整性。