-
模型与贴图:
-
Target
:Target
的Prefabs
保存在Resources/Prefabs
文件下Target
分为不同类型(大小、是否移动以及移动速度的快慢等)
-
arrow
:-
引用商店免费素材
Classical Crossbow
示例如下图所示,其资源保存在
Crossbow
文件夹下 -
弓箭的完整动画流程如下图所示:
-
-
SkyBox
和Terrain
:-
SkyBox
使用的是Unity
上面免费素材Fantasy Skybox Free
,在包管理器Package Manager
中下载并选择想要的Sky Box
即可-
此处我挑选的是
FS003_sunset
这一款Sky Box
-
游戏中呈现的效果:
-
-
Terrain
:-
右键
3D Object
中选择Terrain
创建地形,根据Terrain对应的模块,调整板块数、地形以及树
-
-
-
-
Script
:-
Views
模块:-
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); } }
-
IUerInfo
返回游戏信息的接口-
int GetScore()
表示玩家的得分 -
int GetArrowNum()
返回玩家剩余的箭矢数量 -
string GetMessage()
返回的需要显示的文字信息
public interface IUserAction { int GetScore(); int GetArrowNum(); string GetMessage(); }
-
-
-
Controllers
模块:-
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"; //标记箭为中靶 } } }
-
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; } }
-
-
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; } } }
-
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; //增加新的值 } }
-
ISceneController
加载资源using System.Collections; using System.Collections.Generic; using UnityEngine; // 场景控制器接口,与场景控制有关的函数都在这里 public interface ISceneController { void LoadResources(); // 加载资源 }
-
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); } } }
-
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); } }
-
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; } }
-
-
-
Actions
模块:-
SSActionManager
负责管理和更新多个SSAction
的执行Update
用于添加新动作、更新和销毁动作、销毁已标记删除的动作FixedUpdate
是Unity的另一个生命周期方法,它每秒调用固定次数,适合处理物理更新RunAction
将动作与目标gameobject
和transform
绑定,并设置动作的回调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) { //执行完飞行动作 } }
-
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(); } }
-
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); }
-
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的方法运行动作。 } }
-
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); } } }
-
-
-
最终效果展示:
-
项目保存地址以及项目展示:
- github地址:https://github.com/ashcinder/Unity-
- B站视频演示:https://github.com/ashcinder/unity_final_lab