Unity期末大作业

  1. 模型与贴图:

    1. Target

      • TargetPrefabs保存在Resources/Prefabs文件下
      • Target分为不同类型(大小、是否移动以及移动速度的快慢等)
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
    2. arrow

      • 引用商店免费素材 Classical Crossbow

        示例如下图所示,其资源保存在Crossbow文件夹下

        在这里插入图片描述

      • 弓箭的完整动画流程如下图所示:
        在这里插入图片描述

    3. SkyBoxTerrain

      • SkyBox使用的是Unity上面免费素材Fantasy Skybox Free,在包管理器Package Manager中下载并选择想要的Sky Box即可

        • 此处我挑选的是FS003_sunset这一款Sky Box

          在这里插入图片描述

        • 游戏中呈现的效果:

          在这里插入图片描述

      • Terrain

        • 右键3D Object中选择Terrain创建地形,根据Terrain对应的模块,调整板块数、地形以及树

          在这里插入图片描述

        在这里插入图片描述

  2. Script

    1. Views模块:

      1. UserGUI负责在游戏画面上绘制用户界面元素

        通过 OnGUI() 方法在屏幕上渲染 UI 元素

        负责在游戏画面上绘制用户界面元素,显示玩家的得分、剩余箭矢数和其他游戏信息

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        public class UserGUI : MonoBehaviour
        {
            private IUserAction action;
            GUIStyle style1 = new GUIStyle();
            GUIStyle style2 = new GUIStyle();
            public string message = "";        // 显示的信息
            int screenWidth, screenHeight;
        
            void Start()
            {
                action = this.transform.gameObject.GetComponent<IUserAction>();
        
                style1.normal.textColor = Color.red;
                style1.fontSize = 30;
        
                style2.normal.textColor = Color.yellow;
                style2.fontSize = 40;
            }
        
            void Update()
            {
                screenWidth = Screen.width;
                screenHeight = Screen.height;
            }
        
            private void OnGUI()
            {
                //显示各种信息
                GUI.Label(new Rect(20,10,150,50),"得分: ",style1);
                GUI.Label(new Rect(100,10,150,50),action.GetScore().ToString(),style2);
            
                GUI.Label(new Rect(20,50,150,50),"剩余箭矢数: ",style1);
                GUI.Label(new Rect(200,50,150,50),action.GetArrowNum().ToString(),style2);
        
                GUI.Label(new Rect(150,100,150,50),action.GetMessage(),style1);
        
                GUI.Label(new Rect(screenWidth/2+10,screenHeight/2,150,50),"o",style2);
            }
        }
        
      2. IUerInfo返回游戏信息的接口

        • int GetScore() 表示玩家的得分
        • int GetArrowNum() 返回玩家剩余的箭矢数量
        • string GetMessage() 返回的需要显示的文字信息
        public interface IUserAction
        {
            int GetScore();
            int GetArrowNum();
            string GetMessage();
        }
        
    2. Controllers模块:

      1. TargetController处理箭矢与靶子之间的碰撞逻辑

        • 在碰撞发生时,它会停止箭的运动,使用运动学控制让箭停留在靶子上,同时记录得分,并将箭标记为已命中靶子
        • 通过使用ScoreRecorder类,它可以记录和更新玩家的得分
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 靶子的控制器
        public class TargetController : MonoBehaviour        // 用于处理靶子的碰撞
        {
            public int RingScore = 0;    //当前靶子或环的分值
        
            public ScoreRecorder sc_recorder;
        
            void Start(){
                sc_recorder = Singleton<ScoreRecorder>.Instance;
            }
        
            void Update(){  
            }
        
            void OnCollisionEnter(Collision collision)    // 检测碰撞
            {
                Transform arrow = collision.gameObject.transform;        // 得到箭身
                if(arrow == null) return;
                if(arrow.tag == "arrow"){
                    //将箭的速度设为0
                    arrow.GetComponent<Rigidbody>().velocity = new Vector3(0,0,0);
                    //使用运动学运动控制
                    arrow.GetComponent<Rigidbody>().isKinematic = true;
                    arrow.transform.rotation = Quaternion.Euler(0, 0, 0);    // 使箭的旋转角度为0
                    arrow.transform.parent = this.transform;                // 将箭和靶子绑定
                    sc_recorder.RecordScore(RingScore);         //计分
                    arrow.tag = "onTarget";            //标记箭为中靶
                }
            }
        }
        
      2. SSDirector场景切换

        • 用于管理游戏中的场景控制器

        • 它的核心功能是通过 getInstance 方法实现单例模式,确保游戏中只有一个导演实例

        • 并且提供了一个属性 currentSceneController 用于存储当前场景的控制器。

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 导演类,控制场景切换,模板不用管
        public class SSDirector : System.Object
        {
            private static SSDirector _instance;
            public ISceneController currentSceneController {get; set;}
          
            public static SSDirector getInstance() {          // 获取导演实例
                if (_instance == null) {
                    _instance = new SSDirector();
                }
                return _instance;
            }
        }
        
      3. Singleton场景单实例模板

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 场景单实例模板,不用管
        public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
        {
        
        	protected static T instance;
        
        	public static T Instance {  
        		get {  
        			if (instance == null) { 
        				instance = (T)FindObjectOfType (typeof(T));  
        				if (instance == null) {  
        					Debug.LogError ("An instance of " + typeof(T) +
        					" is needed in the scene, but there is none.");  
        				}  
        			}  
        			return instance;  
        		}  
        	}
        }
        
        
      4. ScoreRecorder记录和更新分数

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 计分器类
        public class ScoreRecorder : MonoBehaviour
        {
            public int score;
            void Start()
            {
                score = 0;
            }
        
            public void RecordScore(int ringscore)
            {
               score += ringscore;        //增加新的值
            }
        }
        
      5. ISceneController加载资源

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 场景控制器接口,与场景控制有关的函数都在这里
        public interface ISceneController
        {
            void LoadResources();    // 加载资源
        }
        
      6. BowController控制了弓的移动、视角旋转和传送到不同的射击位置

        • Move()方法通过Input.GetAxis("Vertical")Input.GetAxis("Horizontal")获取用户输入,分别控制弓的前进/后退和左右移动
        • View()方法控制弓的旋转,通过获取鼠标的X轴和Y轴的移动来调整视角
        • transport()方法处理玩家按下键盘上的不同按键时,弓会传送到不同的位置
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 挂载在弩上,控制弩的移动和视角的旋转、以及传送到指定的射击位置
        public class BowController : MonoBehaviour   
        {
            public float moveSpeed = 6;    //速度:每秒移动6个单位长度
            public float angularSpeed = 90;    //角速度:每秒旋转90度
            public float jumpForce = 200f;    //跳跃参数
            public float horizontalRotateSensitivity = 5;    //水平视角灵敏度
            public float verticalRotateSensitivity = 5;    //垂直视角灵敏度
            private float xRotation = 0f;         // x旋转角度
            public bool canshoot = false;        // 是否可以射箭
            // private Rigidbody rb;
            void Start()
            {
                // rb = transform.parent.GetComponent<Rigidbody>();
            }
         
            void FixedUpdate()    
            {
                Move();
                View();
                transport();
            }
            public float jumpHeight = 10.0f;
            void Move()      // 移动
            {
                float v = Input.GetAxis("Vertical");
                float h = Input.GetAxis("Horizontal");
         
                transform.parent.Translate(Vector3.forward * v * Time.deltaTime * moveSpeed);
                transform.parent.Translate(Vector3.right * h * Time.deltaTime * moveSpeed);
        
                // if (Input.GetKeyDown(KeyCode.Space))
                // {
                //     rb.AddForce(Vector3.up * Mathf.Sqrt(jumpHeight * -2.0f * Physics.gravity.y), ForceMode.VelocityChange);
                // }  
            }
        
            void SetCursorToCentre()   // 锁定鼠标到屏幕中心
            {
                //锁定鼠标后再解锁,鼠标将自动回到屏幕中心
                Cursor.lockState = CursorLockMode.Locked;
                Cursor.lockState = CursorLockMode.None;
                //隐藏鼠标
                Cursor.visible = false;
            }
        
            void View()    // 控制视角
            {
                SetCursorToCentre();        //锁定鼠标到屏幕中心
            
                float mouseX = Input.GetAxis("Mouse X") * Time.deltaTime * angularSpeed* horizontalRotateSensitivity;
                transform.parent.Rotate(Vector3.up * mouseX);     // "人"水平旋转
        
                float mouseY = Input.GetAxis("Mouse Y") * Time.deltaTime * angularSpeed * verticalRotateSensitivity;
                xRotation -= mouseY;
                xRotation = Mathf.Clamp(xRotation, -45f, 45f);          // 限制上下视角
                transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);     // "人"不动,弓上下移动
            }
        
            void transport()     // 传送到指定的射击位置
            {
                if (Input.GetKeyDown(KeyCode.Alpha1))            // 静态靶子区域
                {
                    canshoot = true;
                    transform.parent.rotation = Quaternion.Euler(0, 0, 0);
                    transform.parent.position = new Vector3(120, 1, 70);      
                    RenderSettings.skybox = Resources.Load<Material>("Materials/SkyboxMaterial1");
                }
                if (Input.GetKeyDown(KeyCode.Alpha2))            // 动态靶子区域
                {
                    canshoot = true;
                    transform.parent.rotation = Quaternion.Euler(0, 180, 0);
                    transform.parent.position = new Vector3(-120, 1, -70);   
                    RenderSettings.skybox = Resources.Load<Material>("Materials/SkyboxMaterial2");
                }
                if (Input.GetKeyDown(KeyCode.Alpha3))            // 静态大靶子区域
                {
                    canshoot = true;
                    transform.parent.rotation = Quaternion.Euler(0, 0, 0);
                    transform.parent.position = new Vector3(-120, 1, 70);   
                    RenderSettings.skybox = Resources.Load<Material>("Materials/SkyboxMaterial3");
                }
                if (Input.GetKeyDown(KeyCode.Alpha4))           // 未完待续、、、
                {
                    canshoot = true;
                    transform.parent.rotation = Quaternion.Euler(0, 180, 0);
                    transform.parent.position = new Vector3(120, 1, -70);   
                    RenderSettings.skybox = Resources.Load<Material>("Materials/SkyboxMaterial2");
                }
                if (Input.GetKeyDown(KeyCode.B))                // 回到原始位置
                {
                    canshoot = false;
                    transform.parent.position = new Vector3(0, 1, -8);
                }
            }
          
        }
        
      7. ArrowFactory用于在游戏中创建和回收箭对象

        • Start() 方法是 Unity 中的一个生命周期方法,它会在游戏对象初始化时被调用一次
        • Update() 方法是 Unity 中的另一个生命周期方法,它会在每一帧调用。由于这里并没有具体的逻辑,Update() 方法是空的
        • GetArrow() 方法负责创建箭并将其放置到弓的指定位置,返回这个创建出来的箭对象
        • RecycleArrow()该方法用于回收已经使用过的箭,首先将箭设置为不可见,然后销毁该箭对象。
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 箭的工厂,用于创建箭和回收箭
        public class ArrowFactory : MonoBehaviour
        {
            public ArrowController arrowCtrl;
            public GameObject arrow;
            void Start()
            {
                arrowCtrl = (ArrowController)SSDirector.getInstance().currentSceneController;
            }
        
            void Update(){
            }
        
            public GameObject GetArrow(){    // 获取空闲的箭
                arrow = GameObject.Instantiate(Resources.Load("Prefabs/Arrow", typeof(GameObject))) as GameObject;
                //得到弓箭上搭箭的位置                       
                Transform bow_mid = arrowCtrl.bow.transform.GetChild(4);   // 获得箭应该放置的位置
        
                arrow.transform.position = bow_mid.transform.position;               //将箭的位置设置为弓中间的位置
                arrow.transform.rotation = arrowCtrl.bow.transform.rotation;    // 将箭的旋转角度设置为弓的旋转角度
        
                arrow.transform.parent = arrowCtrl.bow.transform;        //箭随弓的位置变化
                arrow.gameObject.SetActive(false);
        
                return arrow;
            }
        
            public void RecycleArrow(GameObject arrow)        // 回收箭
            {
                arrow.SetActive(false);
                DestroyImmediate(arrow);
            }
        }
        
      8. ArrowController控制弓箭射击,核心功能的实现

        • ArrowController属性

          • arrow_manager: 管理箭的射击动作。
          • factory: 用来生产箭的工厂。
          • main_camera: 游戏主相机,用来设置箭的射击方向。
          • recorder: 记录分数的对象。
          • bow: 弓弩对象。
          • target1, target2, target3, target4, large_target: 目标靶子,包括静态靶子、移动靶子和一个大型靶子。
          • arrow: 当前射出的箭。
          • message: 用于显示信息的字符串(例如:弓箭装载完成的提示)。
          • arrow_num: 当前弓中剩余的箭数。
          • ani: 动画控制器,用来处理射击和弓箭拉弓的动画。
          • arrows: 用来存储所有当前可用的箭。
          • flyed: 存储已发射出去的箭。
        • LoadResources()方法负责加载游戏中的资源:摄像机和弓弩通过transform.GetChild()获取

        • LoadTarget()函数来加载各种靶子(静态、小型、大型、移动等),并设置它们的位置和分数

        • Start函数中,脚本初始化了弓箭工厂、计分器、用户界面GUI、弓弩控制器等组件,并且加载了资源

        • Update每一帧都进行更新

          • 调用Shoot()方法进行射击操作
          • 调用HitGround()方法检查箭是否落地,若落地则将箭的刚体设置为isKinematic,停止物理作用
          • 如果飞出去的箭超过10个,或者箭的y坐标低于-10(即掉落到地面以下),就回收这支箭
          • R键时,调用LoadArrow()重新加载10支箭
        • Shoot()方法控制射击行为

          • 如果没有箭,提示“请按R键以装载弓箭”
          • 如果可以射击arrows.Count > 0,则调用aniShoot()方法进行动画控制和实际射击
        • LoadArrow() 用于重新加载箭矢。

        • HitGround() 用于检查箭是否已经落地,并进行相应的处理(如停留在地面并禁用物理模拟)。

        • GetScore() 返回当前的得分。

        • GetArrowNum() 返回剩余箭矢的数量。

        • GetMessage() 用于获取当前的提示信息或状态。

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        public class ArrowController : MonoBehaviour, ISceneController, IUserAction
        {
            public CCShootManager arrow_manager;    //箭的动作管理者
            public ArrowFactory factory;    //箭的工厂
            public GameObject main_camera;       // 主相机
            public ScoreRecorder recorder;        // 计分对象
            public GameObject bow;     // 弓弩
            public GameObject target1, target2, target3, target4, large_target; // 四种不同的靶子和一个巨型靶子
            public GameObject arrow;       // 当前射出的箭
            public string message = "";   // 用于显示的信息
            private int arrow_num = 0;    // 装载的箭的数量
            public Animator ani;          // 动画控制器
            private List<GameObject> arrows = new List<GameObject>();    // 箭的队列
            private List<GameObject> flyed = new List<GameObject>();    // 飞出去的箭的队列
        
            public float longPressDuration = 2.0f; // 设置长按持续时间
            private bool isLongPressing = false;       // 是否正在长按
            private float pressTime = 0f;         // 长按的时间
            public int state = 0;  // 0-普通状态,1-拉弓状态,2-蓄力状态
            public float power = 0f;  // 拉弓的力度
        
            //加载箭和靶子
            public void LoadResources()
            {
                main_camera = transform.GetChild(5).gameObject;   // 获取摄像机
                bow = this.gameObject;
                // 加载靶子
                for(int i = 0; i < 3; ++i)    // 3 * 2 = 12个靶子
                {
                    target1 = LoadTarget("target", 10 + i, new Vector3(100+20*i, 20, 140+5*i)); // 静态小靶子
                    target2 = LoadTarget("big_target", 5-i, new Vector3(100+20*i, 10, 140-5*i)); // 静态大靶子
                }
                target3 = LoadTarget("target_move", 15, new Vector3(0,0,0)); // 慢速移动靶子, 使用动画,自带位置
                target4 = LoadTarget("target_quick", 20, new Vector3(0,0,0)); // 快速移动靶子,使用动画,自带位置
                large_target = LoadTarget("large_target", 6, new Vector3(-100, 25, 180)); // 静态大靶子
                for(int i = 0; i < 4; ++i)        // 给子对象添加TargetController组件
                {
                    Transform child = large_target.transform.GetChild(i);
                    child.gameObject.AddComponent<TargetController>();
                    child.gameObject.GetComponent<TargetController>().RingScore = 7 + i;       // 不同的环数分数不同
                }
            }
        
            GameObject LoadTarget(string name, int score, Vector3 pos)    // 加载靶子
            {
                GameObject target = GameObject.Instantiate(Resources.Load("Prefabs/"+name, typeof(GameObject))) as GameObject;    // 从预制中获得靶子
                target.transform.position = pos;
                target.AddComponent<TargetController>();                 // 给靶子添加TargetController组件
                target.GetComponent<TargetController>().RingScore = score;
                return target;
            }
        
            //进行资源加载
            void Start()
            {
                arrow = null;   
                SSDirector director = SSDirector.getInstance();
                director.currentSceneController = this;
                director.currentSceneController.LoadResources();
                gameObject.AddComponent<ArrowFactory>();
                gameObject.AddComponent<ScoreRecorder>();
                gameObject.AddComponent<UserGUI>(); 
                gameObject.AddComponent<BowController>();
                factory = Singleton<ArrowFactory>.Instance;
                recorder = Singleton<ScoreRecorder>.Instance;
                arrow_manager = this.gameObject.AddComponent<CCShootManager>() as CCShootManager; 
                ani = GetComponent<Animator>();
            }
        
            void Update()
            {
                Shoot();              // 射击
                HitGround();           // 检测箭是否射中地面
                for(int i = 0; i < flyed.Count; ++i)         // 用于维护已经射出去的箭的队列
                {   
                    GameObject t_arrow = flyed[i];
                    if(flyed.Count > 10 || t_arrow.transform.position.y < -10)   // 箭的数量大于10或者箭的位置低于-10
                    {   // 删除箭
                        flyed.RemoveAt(i);
                        factory.RecycleArrow(t_arrow);
                    }
                }
                if(Input.GetKeyDown(KeyCode.R))         // 按R换弹
                {   //从工厂得到箭
                    LoadArrow();
                    message = "弩箭已填充完毕";
                }
            }
        
            public void Shoot()   // 射击
            {
                if(arrows.Count > 0){    // 确保有箭
                    if(!gameObject.GetComponent<BowController>().canshoot && (Input.GetMouseButtonDown(0) || Input.GetButtonDown("Fire2"))){
                        message = "请按1、2、3到指定地点射击";
                    }
                    else{
                        aniShoot();
                    } 
                }
                else{
                    message = "请按R键以装载弓箭";
                }
            }
        
            public void aniShoot(){    // 射击的动画
                if (Input.GetMouseButtonDown(0) && state==0) // 监测鼠标左键按下
                    {
                        message = "";
                        transform.GetChild(4).gameObject.SetActive(true);           // 设置动作中的箭可见
                        isLongPressing = true;
                        ani.SetTrigger("pull");
                        pressTime = Time.time;
                        state = 1;
                    }
                    if (Input.GetMouseButtonUp(0) && state==1) // 监测鼠标左键抬起
                    {
                        isLongPressing = false;
                        float duration = Time.time - pressTime;
                        if (duration < longPressDuration){                  // 执行普通点击操作
                            power = duration/2;
                        }
                        else{                    // 拉满了
                            power = 1.0f;
                        }
                        ani.SetFloat("power", power);
                        ani.SetTrigger("hold");
                        state = 2;
                    }
                    if (isLongPressing && Time.time - pressTime > longPressDuration)   // 长按但是未抬起,且持续时间超过设定值
                    {
                        // 长按操作
                        isLongPressing = false;
                        power = 1.0f;
                        ani.SetFloat("power", power);
                        ani.SetTrigger("hold");
                    }
                    if (Input.GetButtonDown("Fire2") && state==2){       // 鼠标右键,攻击2
                        transform.GetChild(4).gameObject.SetActive(false);          // 设置动作中的箭不可见
                        ani.SetTrigger("shoot");
                        arrow = arrows[0];
                        arrow.SetActive(true);
                        flyed.Add(arrow);
                        arrows.RemoveAt(0);
                        arrow_manager.ArrowShoot(arrow, main_camera.transform.forward,power);
                        ani.SetFloat("power", 1.0f);        // 恢复力度
                        arrow_num -= 1;
                        state = 0;
                    }
            }
        
            public void LoadArrow(){          // 获得10支箭
                arrow_num = 10;
                while(arrows.Count!=0){     // 清空队列   
                    factory.RecycleArrow(arrows[0]);
                    arrows.RemoveAt(0);
                }
                for(int i=0;i<10;i++){
                    GameObject arrow = factory.GetArrow();
                    arrows.Add(arrow);
                }
            }
        
            public void HitGround()
            {
                RaycastHit hit;
                if (arrow != null && Physics.Raycast(arrow.transform.position, Vector3.down, out hit, 2f))
                {
                    if (hit.collider.gameObject.name == "Terrain")
                    {
                        arrow.tag = "ground";
        
                        // 设置运动学属性
                        Rigidbody arrowRigidbody = arrow.GetComponent<Rigidbody>();
                        arrowRigidbody.isKinematic = true;
        
                        // 手动调整箭的Transform以模拟停止
                        arrow.transform.position = hit.point;
                        arrow.transform.rotation = Quaternion.identity;
                    }
                }
            }
        
        
            //返回当前分数
            public int GetScore(){
                return recorder.score;
            }
        
            //得到剩余的箭的数量
            public int GetArrowNum(){
                return arrow_num;
            }
        
            // 显示的信息
            public string GetMessage(){
                return message;
            }
        }
        
        
        
    3. Actions模块:

      1. SSActionManager负责管理和更新多个 SSAction的执行

        • Update用于添加新动作、更新和销毁动作、销毁已标记删除的动作
        • FixedUpdate 是Unity的另一个生命周期方法,它每秒调用固定次数,适合处理物理更新
        • RunAction将动作与目标 gameobjecttransform 绑定,并设置动作的回调 callback,然后将该动作添加到 waitingAdd 列表中,等待在下一帧被执行,最后调用 action.Start() 来初始化动作。
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 模板代码,不用管
        // 动作管理器的基类
        public class SSActionManager : MonoBehaviour
        {
            private Dictionary<int,SSAction> actions = new Dictionary<int,SSAction>();  //执行动作的字典集
            //等待添加的动作
            private List<SSAction> waitingAdd = new List<SSAction>();
            //等待销毁的动作
            private List<int> waitingDelete = new List<int>();
        
            protected void Start(){
            }
        
            // Update每一帧都执行,执行的帧率取决于电脑性能
            protected void Update() 
            {
                //添加新增的动作
                for(int i = 0; i < waitingAdd.Count; ++i)
                {
                    actions[waitingAdd[i].GetInstanceID()] = waitingAdd[i];
                }
                waitingAdd.Clear();
                //销毁需要删除的动作
                foreach (KeyValuePair<int,SSAction> kv in actions){
                    SSAction ac = kv.Value;
                    if(ac.destroy){
                        waitingDelete.Add(ac.GetInstanceID());
                    }
                    //动作不需要销毁则更新
                    else if(ac.enable){
                        ac.Update();
                    }
                }
                //销毁动作
                foreach (int key in waitingDelete){
                    SSAction ac = actions[key];
                    actions.Remove(key);
                    Object.Destroy(ac);
                }
                waitingDelete.Clear();
            }
            // FixedUpdate每秒调用50次,适合用来写物理引擎相关的代码
            protected void FixedUpdate()
            {
                for(int i = 0; i < waitingAdd.Count; ++i)
                {
                    actions[waitingAdd[i].GetInstanceID()] = waitingAdd[i];
                }
                waitingAdd.Clear();
                foreach (KeyValuePair<int,SSAction> kv in actions){
                    SSAction ac = kv.Value;
                    if(ac.destroy){
                        waitingDelete.Add(ac.GetInstanceID());
                    }
                    else if(ac.enable){
                        ac.FixedUpdate();
                    }
                }
        
                foreach (int key in waitingDelete){
                    SSAction ac = actions[key];
                    actions.Remove(key);
                    Object.Destroy(ac);
                }
                waitingDelete.Clear();
            }
        
            //新增一个动作,运行该动作
            public void RunAction(GameObject gameobject, SSAction  action, ISSCallback manager)
            {
                action.gameobject = gameobject;
                action.transform = gameobject.transform;
                action.callback = manager;
                waitingAdd.Add(action);
                action.Start();
        
            }
        
            //回调新的动作类型
            public void SSActionEvent(SSAction source, int param = 0,GameObject arrow = null)
            {
                //执行完飞行动作
            }
        }
        
      2. SSAction作为动作基类,让其他类可以继承它并实现具体的动作逻辑

        • SSAction 是一个动作的基类,旨在为其他具体的动作类提供模板,它包含了一些字段,如 enable(是否启用动作)、destroy(是否销毁动作)和 gameobject(动作对象)等,用于控制动作的状态。
        • 通过 Start()Update()FixedUpdate() 方法,子类可以实现具体的动作逻辑。
        • 其中的回调机制(ISSCallback)允许动作完成后通知其他部分的逻辑。
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 模板代码,不用管
        // 动作的基类
        public class SSAction : ScriptableObject
        {
            public bool enable = true;                      //是否正在进行此动作
            public bool destroy = false;                    //是否需要被销毁
            public GameObject gameobject;                   //动作对象
            public Transform transform;                     //动作对象的transform
            public ISSCallback callback;              //动作完成后的消息通知者
        
            protected SSAction() { }
            //子类可以使用下面这两个函数
            public virtual void Start()
            {
                throw new System.NotImplementedException();
            }
            public virtual void Update()
            {
                throw new System.NotImplementedException();
            }
            public virtual void FixedUpdate()
            {
                throw new System.NotImplementedException();
            }
        }
        
      3. ISSCallback用于事件回调机制

        • 定义了一个回调机制,目的是在动作系统中处理事件的通知。
        • 首先,通过枚举 SSActionEventType 定义了两种动作事件类型:Started(动作开始)和 Completed(动作完成),用来表示动作的不同状态
        • 然后,定义了一个接口 ISSCallback,其中包含一个回调方法 SSActionEvent,该方法会在动作执行过程中被触发
        • 回调方法提供了多个参数,包括触发事件的动作源(SSAction source),事件类型(SSActionEventType events),以及可选的其他参数(如整型、字符串或对象参数),以便在事件发生时传递额外的信息。
        • 通过实现 ISSCallback 接口,其他类可以订阅这些事件,并在动作状态改变时得到通知,从而在游戏逻辑中作出响应。
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        public enum SSActionEventType:int {Started, Completed}   // 枚举动作事件类型
        public interface ISSCallback
        {
            //回调函数
            void SSActionEvent(SSAction source,
                SSActionEventType events = SSActionEventType.Completed,
                int intParam = 0,
                string strParam = null,
                Object objectParam = null);
        }
        
      4. CCShootManager主要负责射箭动作的管理与执行

        • CCShootManager 类通过继承 SSActionManager 和实现 ISSCallback 接口,管理射箭动作并提供回调机制,能够在箭矢射击过程中处理不同的状态变化
        • 通过 ArrowShoot 方法,开发者可以方便地启动射箭动作,并且该类能够通过 SSActionEvent 回调机制接收并处理箭矢动作的完成事件
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 射箭动作的管理器
        public class CCShootManager : SSActionManager, ISSCallback
        {
            //箭飞行的动作
            private CCShootAction shoot;                            
            protected new void Start(){
            }
        
            public void SSActionEvent(SSAction source,  
                SSActionEventType events = SSActionEventType.Completed,
                int intParam = 0,
                string strParam = null,
                Object objectParam = null) {
            }
        
            //箭飞行
            public void ArrowShoot(GameObject arrow, Vector3 impulseDirection, float power)          // 游戏对象、力的方向、力的大小
            {
                shoot = CCShootAction.GetSSAction(impulseDirection, power);        //实例化一个射箭动作。
                RunAction(arrow, shoot, this);                  //调用SSActionmanager的方法运行动作。
            }
        }
        
      5. CCShootAction实现射箭动作

        • CCShootAction 类通过继承自 SSAction 类,负责实现射箭动作的具体行为
        • 它包括计算箭矢的发射冲量、通过物理引擎控制箭矢的运动、以及在箭矢完成任务后销毁动作的生命周期管理
        • 该类在射箭动作发生时触发回调通知,使得管理器能够根据事件执行相关的游戏逻辑(如清除箭矢、更新分数等)
        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        // 射箭动作
        public class CCShootAction : SSAction
        {
            private Vector3 pulseForce;  //射箭提供的冲量
            private CCShootAction(){}
        
            public static CCShootAction GetSSAction(Vector3 impulseDirection,float power)      //获得一个射箭动作实例
            {
                CCShootAction shootarrow = CreateInstance<CCShootAction>();
                shootarrow.pulseForce = impulseDirection.normalized * power;          // 射箭提供的冲量(方向*力度)
                return shootarrow;
            }
        
            public override void Start()
            {
                gameobject.transform.parent = null;        // 摆脱跟随弓移动
                gameobject.GetComponent<Rigidbody>().useGravity = true;                  //  发射的时候才开启重力
                gameobject.GetComponent<Rigidbody>().velocity = Vector3.zero;               // 初速度为0
                gameobject.GetComponent<Rigidbody>().AddForce(pulseForce,ForceMode.Impulse);        //添加冲量
                //关闭运动学控制
                gameobject.GetComponent<Rigidbody>().isKinematic = false;      // 此时箭的运动由物理引擎控制
            }
        
            public  override void Update(){
            
            }
        
            public override void FixedUpdate(){   // 判断箭是否飞出场景、落在地上或者射中靶子
                if(!gameobject || this.gameobject.tag == "ground" || this.gameobject.tag == "onTarget"  )  
                {
                    this.destroy = true;     // 摧毁动作
                    this.callback.SSActionEvent(this);
                }
            }
        }
        
  3. 最终效果展示:

在这里插入图片描述
在这里插入图片描述

  1. 项目保存地址以及项目展示:

### Unity期末大作业示例教程 #### 游戏角色控制实现 在游戏中,玩家通常通过键盘输入来控制游戏角色的动作。对于Unity中的角色控制器,可以通过编写C#脚本来定义这些行为。 ```csharp using UnityEngine; public class PlayerController : MonoBehaviour { public float speed = 10f; void Update() { float moveHorizontal = Input.GetAxis("Horizontal"); Vector3 movement = new Vector3(moveHorizontal, 0.0f, 0.0f); transform.Translate(movement * speed * Time.deltaTime); if (Input.GetKeyDown(KeyCode.Space)) { GetComponent<Rigidbody>().AddForce(Vector3.up * 500); } } } ``` 此段代码展示了如何让玩家利用方向键左右移动以及按下空格键使角色跳跃[^2]。 #### 障碍物生成机制 为了增加游戏挑战性,在空中随机位置生成下落的障碍物是一个常见设计思路。下面是一份用于创建此类物体的简化版脚本: ```csharp using System.Collections; using UnityEngine; public class ObstacleSpawner : MonoBehaviour { public GameObject obstaclePrefab; //预制体对象 private float spawnRate = 2f; //生成频率 void Start(){ StartCoroutine(SpawnObstacles()); } IEnumerator SpawnObstacles(){ while(true){ Instantiate(obstaclePrefab,new Vector3(Random.Range(-8f,8f),7f,-10f),Quaternion.identity); yield return new WaitForSeconds(spawnRate); } } } ``` 这段程序每隔一段时间就会在一个范围内随机地点实例化一个新的障碍物预制件。 #### 生命值管理逻辑 当玩家碰到任何敌人或危险物品时减少其健康状态是游戏中常见的惩罚措施之一。这里给出了一种简单的生命管理系统实现方式: ```csharp using UnityEngine; using UnityEngine.UI; public class HealthManager : MonoBehaviour{ public int maxHealth=100; private int currentHealth; public Slider healthBarUIElement; void Awake(){ currentHealth=maxHealth; SetMaxHealth(); } public void TakeDamage(int damageAmount){ currentHealth-=damageAmount; Debug.Log($"Player took {damageAmount} points of damage."); CheckIfDead(); UpdateHealthBar(); } private void SetMaxHealth(){ healthBarUIElement.maxValue=maxHealth; healthBarUIElement.value=maxHealth; } private void UpdateHealthBar(){ healthBarUIElement.value=currentHealth; } private void CheckIfDead(){ if(currentHealth<=0){ Die(); } } private void Die(){ Debug.Log("Game Over!"); //处理死亡后的场景切换或其他操作... } } ``` 上述代码片段实现了基本的生命条显示功能,并在玩家受到伤害后更新界面并判断是否已阵亡。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值