用unity设计地图射箭的小游戏,用了对象池,单实例等方法,暂存,会详细补充
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);
}
}
}
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的方法运行动作。
}
}
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);
}
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();
}
}
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)
{
//执行完飞行动作
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowController : MonoBehaviour, ISceneController, IUserAction
{
public GameObject main_camera; // 主相机
public CCShootManager arrow_manager; //箭的动作管理者
public ArrowFactory factory; //箭的工厂
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.parent.GetChild(0).gameObject; // 获取摄像机
bow = this.gameObject;
// 获取地形对象
Terrain terrain = GameObject.Find("Terrain").GetComponent<Terrain>();
// 加载随机靶子
for (int i = 0; i < 15; ++i) {
LoadTargetOnTerrain("target", 10 + i, terrain);
}
}
GameObject LoadTargetOnTerrain(string name, int score, Terrain terrain) {
// 随机生成靶子在地形上的位置
float terrainWidth = terrain.terrainData.size.x;
float terrainLength = terrain.terrainData.size.z;
// 随机生成 (x, z) 坐标
float randomX = Random.Range(0, terrainWidth);
float randomZ = Random.Range(0, terrainLength);
// 获取地形在 (x, z) 的高度
float terrainHeight = terrain.SampleHeight(new Vector3(randomX, 0, randomZ));
// 靶子的位置在地形表面上方 3f
Vector3 targetPosition = new Vector3(randomX, terrainHeight + 3f, randomZ);
// 加载靶子
GameObject target = GameObject.Instantiate(Resources.Load(name, typeof(GameObject))) as GameObject;
target.transform.position = targetPosition;
target.AddComponent<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()
{
//Debug.Log("Update running"); // 检查 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.GetKeyDown(KeyCode.Space) && state == 0) {
// 开始拉弓
message = "";
transform.GetChild(3).gameObject.SetActive(true);
isLongPressing = true;
ani.SetTrigger("pull");
pressTime = Time.time;
state = 1;
}
if (Input.GetKey(KeyCode.Space) && state == 1) {
// 计算拉弓持续时间和力度
float duration = Time.time - pressTime;
power = Mathf.Clamp(duration / longPressDuration, 0, 1);
ani.SetFloat("power", power);
if (duration >= longPressDuration || power >= 1.0f) {
// 达到最大蓄力,进入“hold”状态
ani.SetTrigger("hold");
state = 2;
}
}
if (Input.GetKeyUp(KeyCode.Space)) {
if (state == 1 || state == 2) {
// 松开空格,执行射击
isLongPressing = false;
ani.SetTrigger("shoot");
// 设置动作中的箭不可见
transform.GetChild(3).gameObject.SetActive(false);
// 发射箭矢
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, 0.2f))
{
// 如果射线与地面相交
if (hit.collider.gameObject.name == "Terrain")
{
arrow.tag = "ground";
//将箭的速度设为0
arrow.GetComponent<Rigidbody>().velocity = new Vector3(0,0,0);
//使用运动学运动控制
arrow.GetComponent<Rigidbody>().isKinematic = true;
}
}
}
//返回当前分数
public int GetScore(){
return recorder.score;
}
//得到剩余的箭的数量
public int GetArrowNum(){
return arrow_num;
}
// 显示的信息
public string GetMessage(){
return message;
}
}
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("Arrow", typeof(GameObject))) as GameObject;
//得到弓箭上搭箭的位置
Transform bow_mid = arrowCtrl.bow.transform.GetChild(3); // 获得箭应该放置的位置
arrow.transform.position = bow_mid.transform.position; //将箭的位置设置为弓中间的位置
arrow.transform.rotation = Quaternion.LookRotation(arrowCtrl.bow.transform.up);
arrow.transform.parent = arrowCtrl.bow.transform; //箭随弓的位置变化
arrow.gameObject.SetActive(false);
return arrow;
}
public void RecycleArrow(GameObject arrow) // 回收箭
{
arrow.SetActive(false);
DestroyImmediate(arrow);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 挂载在子对象上,控制父对象的移动、跳跃和视角旋转
public class BowController : MonoBehaviour
{
public float moveSpeed = 6f; // 移动速度
public float angularSpeed = 90f; // 旋转速度
public float horizontalRotateSensitivity = 5f; // 水平视角灵敏度
public float verticalRotateSensitivity = 5f; // 垂直视角灵敏度
private float xRotation = 0f; // X轴旋转角度
public float jumpForce = 5f; // 跳跃力度
public bool isGrounded = true; // 是否接触地面
public bool canshoot = false; // 是否可以射箭
//private Rigidbody parentRb; // 父对象的 Rigidbody
void Start()
{
// parentRb = transform.parent.GetComponent<Rigidbody>();
// if (parentRb == null)
// {
// Debug.LogError("父对象缺少 Rigidbody 组件");
// }
}
void FixedUpdate()
{
Move(); // 控制移动
View(); // 控制视角
}
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) && isGrounded)
// {
// parentRb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
// isGrounded = false; // 跳跃后设置为未接触地面
// }
}
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); // 控制弓的上下视角
}
// private void OnCollisionEnter(Collision collision)
// {
// if (collision.gameObject.name == "Terrain")
// {
// isGrounded = true;
// }
// }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 场景控制器接口,与场景控制有关的函数都在这里
public interface ISceneController
{
void LoadResources(); // 加载资源
}
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; //增加新的值
}
}
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);
}
}