一.游戏简介:
这是一个简单的打靶游戏,总共有五环,从外到里依次是一到五环,打到一环得一分,二环得二分,依此类推
玩家可以通过方向键或asdw键来控制发射的位置,还有,在游戏当中,还会有风来影响箭的飞行方向
二.游戏效果图:
三.UML图:
四.游戏主要代码和设置说明:
1.游戏中是用了虚拟轴来控制箭的位置,不过移动箭的代码是挂在一个空对象上的,而箭是它的子对象,当箭射出的时候,就把它的parent置为null,这样在
箭射出后就不能控制箭了,移动的代码如下:
void Update () {
float translationY = Input.GetAxis("Vertical") * speed;
float translationX = Input.GetAxis("Horizontal") * speed;
translationY *= Time.deltaTime;
translationX *= Time.deltaTime;
this.gameObject.transform.Translate(0, translationY, 0);
this.gameObject.transform.Translate(-translationX, 0, 0);
}
2.游戏中箭的初始方向和初始速度都是固定的
horizontalSpeed = gameobject.GetComponent<ArrowData>().speed;
direction = Vector3.back;
rigid = this.gameobject.GetComponent<Rigidbody>();
rigid.velocity = direction * horizontalSpeed;
3.游戏中靶是有五个圆柱体和一个空对象组成的,圆柱体是空对象的子对象,通过圆柱体细微的高度差异,就可以显示各个环的颜色,
通过mesh触发器,就可以和箭产生出发事件,各个圆柱的设置差不多,只是其中一个:
4.但各个圆柱的触发区域有重叠的地方,我这里是用一个数组来记录有哪些环出发了,然后选得分最大的那个环
hitedTarget = new bool[5] { false, false, false, false, false };
public void setHitedTarget(int ring)
{
hitedTarget[ring - 1] = true;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.GetComponent<Rigidbody>())
{
other.gameObject.transform.FindChild("head").gameObject.SetActive(false);
other.gameObject.GetComponent<Rigidbody>().isKinematic = true;
sceneController.setHitedTarget((int)this.gameObject.name[0] - (int)'0'); //环的名字是对应第几环
}
}
5.游戏中还会有风产生,风是挂载在箭上的脚本,本来是应该归动作管理器管理的,但这里偷懒,直接挂在箭上
void FixedUpdate() {
if (rigid.useGravity) //当箭射出时,刚体的useGravity属性才会置为true
{
rigid.AddForce(force);
}
}
五.全部源代码
1.ArrowData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowData : MonoBehaviour {
public Vector3 size;
public Color color;
public float speed;
public Vector3 direction;
public bool destroy;
public bool used;
}
2.ArrowFactory.cs
/**
* 这个文件是用来生产飞碟的工厂
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowFactory : MonoBehaviour
{
public GameObject ArrowPrefab;
public GameObject person;
float time = 0;
/**
* used是用来保存正在使用的箭
* free是用来保存未激活的箭
*/
private Dictionary<int, ArrowData> used = new Dictionary<int, ArrowData>();
private List<ArrowData> free = new List<ArrowData>();
private List<int> wait = new List<int>();
private void Awake()
{
ArrowPrefab = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/arrow"), Vector3.zero, Quaternion.identity);
person = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/person"), new Vector3(0,0,3), Quaternion.identity);
ArrowPrefab.SetActive(false);
}
public GameObject GetArrow()
{
GameObject newArrow = null;
if (free.Count > 0)
{
newArrow = free[0].gameObject;
free.Remove(free[0]);
}
else
{
newArrow = GameObject.Instantiate<GameObject>(ArrowPrefab, Vector3.zero, Quaternion.identity);
}
ArrowData data = newArrow.GetComponent<ArrowData>();
data.destroy = false;
data.speed = 15f;
data.used = false;
data.transform.rotation = Quaternion.identity;
newArrow.transform.position = new Vector3(0, 0, 3);
Rigidbody rigid = newArrow.GetComponent<Rigidbody>();
rigid.isKinematic = false;
rigid.useGravity = false;
rigid.velocity = Vector3.zero;
used.Add(data.GetInstanceID(), data);
newArrow.transform.parent = person.transform;
newArrow.SetActive(true);
return newArrow;
}
public void FreeArrow(GameObject Arrow)
{
ArrowData tmp = null;
int key = Arrow.GetComponent<ArrowData>().GetInstanceID();
if (used.ContainsKey(key))
{
tmp = used[key];
}
if (tmp != null)
{
tmp.gameObject.SetActive(false);
free.Add(tmp);
used.Remove(key);
}
}
private void Update()
{
time += Time.deltaTime;
if (time > 10f)
{
clear();
time = 0;
}
foreach (var tmp in used.Values)
{
if (tmp.destroy)
{
wait.Add(tmp.GetInstanceID());
}
}
foreach (int tmp in wait)
{
FreeArrow(used[tmp].gameObject);
}
wait.Clear();
}
public void clear()
{
foreach (var tmp in used.Values)
{
if (tmp.GetComponent<ArrowData>().used)
{
tmp.gameObject.SetActive(false);
tmp.destroy = true;
}
}
}
}
3.CCActionManager.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : SSActionManager, ISSActionCallback {
public FirstSceneControl sceneController;
protected void Start()
{
sceneController = (FirstSceneControl)Director.getInstance().currentSceneControl;
sceneController.actionManager = this;
}
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0,
string strParam = null,
UnityEngine.Object objectParam = null)
{
if (!source.gameobject.activeSelf)
{
source.gameobject.GetComponent<ArrowData>().destroy = true;
}
if (source is CCFlyAction)
{
source.gameobject.GetComponent<ArrowData>().used = true;
sceneController.setGameState(GameState.DART_FINISH);
source.reset();
}
}
public void StartThrow(GameObject Arrow)
{
CCFlyActionFactory cf = Singleton<CCFlyActionFactory>.Instance;
RunAction(Arrow, cf.GetSSAction(), (ISSActionCallback)this);
Arrow.transform.parent = null;
Arrow.GetComponent<Rigidbody>().useGravity = true;
}
}
4.CCFlyAction.cs
/**
* 这个文件是实现飞碟的飞行动作
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCFlyAction : SSAction {
float horizontalSpeed;
Vector3 direction;
Rigidbody rigid;
public override void Start () {
enable = true;
horizontalSpeed = gameobject.GetComponent<ArrowData>().speed;
direction = Vector3.back;
rigid = this.gameobject.GetComponent<Rigidbody>();
rigid.velocity = direction * horizontalSpeed;
rigid.useGravity = false;
}
// Update is called once per frame
public override void FixedUpdate () {
if (gameobject.activeSelf && rigid)
{
if (this.transform.position.y < -4)
{
this.destroy = true;
this.enable = false;
this.gameobject.SetActive(false);
this.callback.SSActionEvent(this);
}
if (rigid.isKinematic)
{
this.enable = false;
this.callback.SSActionEvent(this);
}
}
}
public static CCFlyAction GetCCFlyAction()
{
CCFlyAction action = ScriptableObject.CreateInstance<CCFlyAction>();
return action;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCFlyActionFactory : MonoBehaviour {
private Dictionary<int, SSAction> used = new Dictionary<int, SSAction>();
private List<SSAction> free = new List<SSAction>();
private List<int> wait = new List<int>();
public CCFlyAction Fly;
// Use this for initialization
void Start () {
Fly = CCFlyAction.GetCCFlyAction();
}
private void Update()
{
foreach (var tmp in used.Values)
{
if (tmp.destroy)
{
wait.Add(tmp.GetInstanceID());
}
}
foreach (int tmp in wait)
{
FreeSSAction(used[tmp]);
}
wait.Clear();
}
public SSAction GetSSAction()
{
SSAction action = null;
if (free.Count > 0)
{
action = free[0];
free.Remove(free[0]);
}
else
{
action = ScriptableObject.Instantiate<CCFlyAction>(Fly);
}
used.Add(action.GetInstanceID(), action);
return action;
}
public void FreeSSAction(SSAction action)
{
SSAction tmp = null;
int key = action.GetInstanceID();
if (used.ContainsKey(key))
{
tmp = used[key];
}
if (tmp != null)
{
tmp.reset();
free.Add(tmp);
used.Remove(key);
}
}
public void clear()
{
foreach (var tmp in used.Values)
{
tmp.enable = false;
tmp.destroy = true;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCMoveDartAction : MonoBehaviour {
// Use this for initialization
void Start () {
}
float speed = 4f;
// Update is called once per frame
void Update () {
float translationY = Input.GetAxis("Vertical") * speed;
float translationX = Input.GetAxis("Horizontal") * speed;
translationY *= Time.deltaTime;
translationX *= Time.deltaTime;
this.gameObject.transform.Translate(0, translationY, 0);
this.gameObject.transform.Translate(-translationX, 0, 0);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collision : MonoBehaviour {
public FirstSceneControl sceneController;
// Use this for initialization
void Start() {
sceneController = (FirstSceneControl)Director.getInstance().currentSceneControl;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.GetComponent<Rigidbody>())
{
other.gameObject.transform.FindChild("head").gameObject.SetActive(false);
other.gameObject.GetComponent<Rigidbody>().isKinematic = true;
sceneController.setHitedTarget((int)this.gameObject.name[0] - (int)'0');
}
}
}
/**
* 这个文件是用来场景控制的,负责各个场景的切换,
* 虽然目前只有一个场景
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Director : System.Object {
/**
* currentSceneControl标志目前正在使用的场景
*/
public ISceneControl currentSceneControl { get; set; }
/**
* Director这个类是采用单例模式
*/
private static Director director;
private Director()
{
}
public static Director getInstance()
{
if (director == null)
{
director = new Director();
}
return director;
}
}
/**
* 这个文件是用来控制主游戏场景的
*/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstSceneControl : MonoBehaviour, ISceneControl, IUserAction {
/**
* actionManager是用来指定当前的动作管理器
*/
public CCActionManager actionManager { get; set; }
/**
* scoreRecorder是用来指定当前的记分管理对象的
*/
public ScoreRecorder scoreRecorder { get; set; }
public bool[] hitedTarget = null;
/**
* gameState是用来保存当前的游戏状态
*/
private GameState gameState;
private GameObject currentArrow;
void Awake () {
Director director = Director.getInstance();
director.currentSceneControl = this;
gameState = GameState.DART_START;
hitedTarget = new bool[5] { false, false, false, false, false };
this.gameObject.AddComponent<ScoreRecorder>();
this.gameObject.AddComponent<ArrowFactory>();
this.gameObject.AddComponent<CCFlyActionFactory>();
scoreRecorder = Singleton<ScoreRecorder>.Instance;
director.currentSceneControl.LoadResources();
}
private void Update()
{
/**
* 以下代码用来管理游戏的状态
*/
if (gameState == GameState.DART_FINISH)
{
int ring = check();
scoreRecorder.Record(ring);
gameState = GameState.DART_START;
}
if (gameState == GameState.DART_START)
{
ArrowFactory df = Singleton<ArrowFactory>.Instance;
currentArrow = df.GetArrow();
gameState = GameState.WAIT;
}
if (gameState == GameState.SHOOT)
{
actionManager.StartThrow(currentArrow);
gameState = GameState.RUNNING;
}
}
public void LoadResources()
{
GameObject DartsBoard = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/dartsboard"));
GameObject plane = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/plane"));
}
public string GetWindInfo()
{
float wind = currentArrow.GetComponent<wind>().GetForce();
if (wind < 0)
{
return "Right " + (-wind).ToString();
}
else
{
return "Left " + wind.ToString();
}
}
public int GetScore()
{
return scoreRecorder.score;
}
public GameState getGameState()
{
return gameState;
}
public void setGameState(GameState gs)
{
gameState = gs;
}
public void setHitedTarget(int ring)
{
hitedTarget[ring - 1] = true;
}
public int check()
{
int result = -1;
for (int i = 4; i >= 0; i--)
{
if (hitedTarget[i])
{
result = i;
break;
}
}
for (int i = 4; i >= 0; i--)
{
hitedTarget[i] = false;
}
return result + 1;
}
}
/*
* 场景控制的必须实现的接口
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISceneControl {
void LoadResources();
}
11.ISSActionCallback.cs
/**
* 这个接口负责动作与动作管理器间的通信
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType:int { Started, Competeted }
public interface ISSActionCallback {
void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0,
string strParam = null,
Object objectParam = null);
}
12.IUserAction.cs
/**
* UI界面与场景管理器通信的接口
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum GameState { RUNNING, DART_START, DART_FINISH, WAIT, SHOOT}
public interface IUserAction {
GameState getGameState();
void setGameState(GameState gs);
int GetScore();
string GetWindInfo();
}
13.ScoreRecorder.cs
/**
* 这个类是用来记录玩家得分的
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScoreRecorder : MonoBehaviour {
/**
* score是玩家得到的总分
*/
public int score;
/**
* scoreTable是一个得分的规则表,每种飞碟的颜色对应着一个分数
*/
private Dictionary<int, int> scoreTable = new Dictionary<int, int>();
// Use this for initialization
void Start () {
score = 0;
scoreTable.Add(0, 0);
scoreTable.Add(1, 1);
scoreTable.Add(2, 2);
scoreTable.Add(3, 3);
scoreTable.Add(4, 4);
scoreTable.Add(5, 5);
}
public void Record(int ring)
{
score += scoreTable[ring];
}
public void Reset()
{
score = 0;
}
}
14.Singleton.cs
/**
* 这是一个实现单例模式的模板,所有的MonoBehaviour对象都用这个模板来实现单实例
*/
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;
}
}
}
15.SSAction.cs
/**
* 所有动作的基类
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject {
public bool enable = false;
public bool destroy = false;
public GameObject gameobject { get; set; }
public Transform transform { get; set; }
public ISSActionCallback callback { get; set; }
protected SSAction() { }
public virtual void Start () {
throw new System.NotImplementedException();
}
// Update is called once per frame
public virtual void FixedUpdate () {
throw new System.NotImplementedException();
}
public void reset()
{
enable = false;
destroy = false;
gameobject = null;
transform = null;
callback = null;
}
}
16.SSActionManager.cs
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>(); //动作的删除队列,在这个对象保存的动作会稍后删除
// Update is called once per frame
protected void FixedUpdate()
{
//把等待队列里所有的动作注册到动作管理器里
foreach (SSAction ac in waitingAdd) actions[ac.GetInstanceID()] = ac;
waitingAdd.Clear();
//管理所有的动作,如果动作被标志为删除,则把它加入删除队列,被标志为激活,则调用其对应的Update函数
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);
DestroyObject(ac);
}
waitingDelete.Clear();
}
//初始化一个动作
public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager)
{
action.gameobject = gameobject;
action.transform = gameobject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
}
17.UserGUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UserGUI : MonoBehaviour
{
private IUserAction action;
void Start () {
action = Director.getInstance().currentSceneControl as IUserAction;
}
private void OnGUI()
{
if (GUI.Button(new Rect(900, 500, 90, 90), "shoot") && (action.getGameState() == GameState.WAIT))
{
action.setGameState(GameState.SHOOT);
}
GUI.Label(new Rect(500, 90, 90, 90), "Force: " + action.GetWindInfo());
GUI.Label(new Rect(900, 90, 90, 90), "Score: " + action.GetScore().ToString());
}
}
18.wind.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class wind : MonoBehaviour {
Vector3 force;
Rigidbody rigid;
void Start() {
float xforce = Random.Range(-0.5f, 0.5f);
force = new Vector3(xforce, 0, 0);
rigid = this.gameObject.GetComponent<Rigidbody>();
}
void FixedUpdate() {
if (rigid.useGravity)
{
rigid.AddForce(force);
}
}
public float GetForce()
{
return force.x;
}
}