unity实现鼠标打飞碟(Hit UFO)游戏
程序员文章站
2022-07-12 23:31:45
...
unity实现鼠标打飞碟(Hit UFO)游戏
游戏规则及要求
规则:玩家初始有5条生命,每漏过一个飞碟,生命减一。游戏分数的增加与命中飞碟时的位置有关,越早击中飞碟分数越高。游戏有四档难度,随分数递增。
要求:
- 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
- 近可能使用前面 MVC 结构实现人机交互与游戏模型分离
UML类图
代码框架
基本沿用了牧师与魔鬼的框架,主要增加了场景单实例和飞碟工厂类。
代码分析
与牧师与魔鬼2.0版本类似,将所有动作管理类合并成一个C#文件。许多类与之前的代码类似,主要说明不同的部分。
- CCMoveToAction(管理飞碟飞行的动作)
public class CCMoveToAction : SSAction
{
public float Power = 10;//这个代表发射时的速度
public float Angle = 45;//发射的角度
public float Gravity = -10;//重力加速度
private Vector3 MoveSpeed;//初速度
private Vector3 GritySpeed = Vector3.zero;//重力的速度向量,t时为0
private float dTime;//已经过去的时间
private Vector3 currentAngle;
public DiskFactory diskfactory;
public UserGUI User;
public static CCMoveToAction GetSSAction(float Power,float Angle,float Gravity,Vector3 MoveSpeed)
{
CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
action.Power = Power;
action.Angle = Angle;
action.Gravity = Gravity;
action.MoveSpeed = MoveSpeed;
return action;
}
public override void Update()
{
GritySpeed.y = Gravity * (dTime += Time.fixedDeltaTime);
transform.position += (MoveSpeed + GritySpeed) * Time.fixedDeltaTime;
currentAngle.z = Mathf.Atan((MoveSpeed.y + GritySpeed.y) / MoveSpeed.x) * Mathf.Rad2Deg;
transform.eulerAngles = currentAngle;
if(this.transform.position.y < -7 || this.transform.position.x > 10){
if(this.transform.position.x > 0 && User.HP > 0) User.HP--;
diskfactory.FreeDisk(this.gameobject);
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
public override void Start(){
//User = gameObject.AddComponent<UserGUI>() as UserGUI;
MoveSpeed = Quaternion.Euler(new Vector3(0, 0, Angle)) * Vector3.right * Power;
currentAngle = Vector3.zero;
diskfactory = Singleton<DiskFactory>.Instance;
User = Singleton<UserGUI>.Instance;
}
}
按照飞碟的初始速度、所受到的重力、初始角度,控制飞碟飞行动作。当飞碟飞出屏幕时,表示飞碟飞行动作已做完,回收飞碟。
- MySceneActionManager(飞碟的动作管理类,通过调用该类中的方法使得飞碟飞行)
public class MySceneActionManager : SSActionManager
{
private CCMoveToAction diskfly;
public Controll sceneController;
protected new void Start()
{
sceneController = (Controll)SSDirector.GetInstance().CurrentScenceController;
sceneController.actionManager = this;
}
public void fly_disk(Disk disk){
diskfly = CCMoveToAction.GetSSAction(disk.Power,disk.Angle,disk.Gravity,disk.MoveSpeed);
this.RunAction(disk.getGameObject(), diskfly, this);
}
}
- Singleton(场景单实例,可以为每个 MonoBehaviour子类创建一个对象的实例)
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;
}
}
}
场景单实例的使用很简单,仅需要将 MonoBehaviour 子类对象挂载任何一个游戏对象上即可。然后在任意位置使用代码 Singleton< YourMonoType >.Instance 获得该对象。
- DiskFactory
飞碟工厂类,控制飞碟的产生和回收,当场景控制器需要飞碟时,
飞碟工厂从空闲飞碟列表中寻找飞碟传递给场景控制器,若空闲列表中无飞碟,则飞碟工厂需要产生一个新的飞碟。当飞碟飞出屏幕或被打击后,会调用飞碟工厂回收飞碟的方法。通过工厂类,可以管理不同飞碟的发送和回收
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskFactory : MonoBehaviour
{
//public Disk disk_prefab = null; //飞碟预制体
public List<Disk> used = new List<Disk>(); //正在被使用的飞碟列表
public List<Disk> free = new List<Disk>(); //空闲的飞碟列表
public Disk GetDisk()
{
Disk disk_prefab = null;
int rand = Random.Range(0, 99);
if (free.Count > 10){
disk_prefab = free[0];
disk_prefab.getGameObject().SetActive(true);
//disk_prefab.getGameObject().transform.position = new Vector3(-10,0,0);
free.Remove(free[0]);
}
else {
if (rand % 4 == 0){
disk_prefab = new Disk("disk1");
}
else if(rand % 4 == 1){
disk_prefab = new Disk("disk2");
}
else if(rand % 4 == 2){
disk_prefab = new Disk("disk3");
}
else{
disk_prefab = new Disk("disk4");
}
}
used.Add(disk_prefab);
return disk_prefab;
}
//回收飞碟
public void ResetDisk(Disk disk){
disk.Power = 0;
disk.Angle = 0;
disk.Gravity = 0;
}
public void FreeDisk(GameObject disk)
{
for(int i = 0;i < used.Count; i++)
{
if (disk.GetInstanceID() == used[i].getGameObject().GetInstanceID())
{
ResetDisk(used[i]);
used[i].getGameObject().SetActive(false);
free.Add(used[i]);
used.Remove(used[i]);
break;
}
}
}
}
- Disk(飞碟类)
定义了飞碟所拥有的各项属性,包括受到的力度、飞行角度、所受重力和移动速度。
public class Disk
{
public GameObject disk; //射击此飞碟得分
public float Power = 3;
public float Angle = 15;
public float Gravity = -0.5f;
public Vector3 MoveSpeed;
public Disk(string name){
if(name == "disk1"){
disk = Object.Instantiate(Resources.Load("disk1", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, -90, 0)) as GameObject;
}
else if(name == "disk2"){
disk = Object.Instantiate(Resources.Load("disk2", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, -90, 0)) as GameObject;
}
else if(name == "disk3"){
disk = Object.Instantiate(Resources.Load("disk3", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, -90, 0)) as GameObject;
}
else if(name == "disk4"){
disk = Object.Instantiate(Resources.Load("disk4", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, -90, 0)) as GameObject;
}
}
public GameObject getGameObject() { return disk; }
}
- Controll 场景控制器
场景控制器通过设置游戏状态和游戏所进行的轮数从而控制飞碟的发送频率、一次发送所发送飞碟的个数和飞碟的属性。游戏每达到10分难度就会提升一档,总共有四档难度,飞碟的发送频率、速度、角度的变化幅度、发送位置的变化幅度都会随着难度的提升而提升。当游戏进入第四档时,则会一次发送两个飞碟。
通过射线来判断飞碟是否被击中,若击中飞碟,则得分增加,否则若飞碟飞出屏幕,则生命会减少。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Controll : MonoBehaviour,ISceneController,IUserAction
{
private int gamestate = 0; // 0 游戏未开始,1 游戏进行 ,2 游戏开始
private int round = 1;
private int score = 0;
private float time = 2f;
private float i = 0;
public UserGUI User;
public MySceneActionManager actionManager;
//public Disk disk;
public DiskFactory diskfactory;
void Start()
{
SSDirector director = SSDirector.GetInstance();
director.CurrentScenceController = this;
User = gameObject.AddComponent<UserGUI>() as UserGUI;
diskfactory = Singleton<DiskFactory>.Instance;
actionManager = gameObject.AddComponent<MySceneActionManager>() as MySceneActionManager;
//LoadResources();
}
void Update(){
if (gamestate == 2){
gamestate = 1;
}
if (gamestate == 1){
if (i >= time){
if (score > 10 && round == 1){
time -= 0.3f;
round = 2;
}
else if(score > 20 && round == 2){
time -= 0.5f;
round = 3;
}
else if(score > 30 && round == 3){
time += 0.3f;
round = 4;
}
SendDisk();
i = 0;
}
i += Time.deltaTime;
}
}
public void LoadResources(){
}
public void SendDisk(){
Disk disk = diskfactory.GetDisk();
if (round == 1){
disk.Power = Random.Range(2.5f, 3.5f);
disk.Angle = Random.Range(10f, 30f);
disk.Gravity = Random.Range(-0.5f, -0.8f);
disk.disk.transform.position = new Vector3(-10,Random.Range(-3f, 2f),0);
}
else if (round == 2){
disk.Power = Random.Range(3.5f, 5f);
disk.Angle = Random.Range(5f, 35f);
disk.Gravity = Random.Range(-0.6f, -1f);
disk.disk.transform.position = new Vector3(-10,Random.Range(-4f, 1f),0);
}
else if (round == 3){
disk.Power = Random.Range(5.5f, 8f);
disk.Angle = Random.Range(5f, 35f);
disk.Gravity = Random.Range(-1f, -1.5f);
disk.disk.transform.position = new Vector3(-10,Random.Range(-5f, 3f),0);
}
else if (round == 4){
disk.Power = Random.Range(7f, 10f);
disk.Angle = Random.Range(5f, 35f);
disk.Gravity = Random.Range(-2f, -2.5f);
disk.disk.transform.position = new Vector3(-10,Random.Range(-5f, 2f),0);
Disk disk2 = diskfactory.GetDisk();
disk2.Power = Random.Range(6f, 8f);
disk2.Angle = Random.Range(5f, 35f);
disk2.Gravity = Random.Range(-1f, -3f);
disk2.disk.transform.position = new Vector3(-10,Random.Range(-4.5f, 1f),0);
actionManager.fly_disk(disk2);
}
actionManager.fly_disk(disk);
}
public void Restart(){
score = 0;
gamestate = 2;
}
public void Stopgame(){
gamestate = 0;
round = 1;
time = 2f;
}
public void Startgame(){
gamestate = 2;
}
public bool Hit(){
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit)) {
if(hit.collider.gameObject.transform.position.x < 3 && gamestate > 0)score+=2;
else if(hit.collider.gameObject.transform.position.x >= 3 && gamestate > 0) score+=1;
hit.collider.gameObject.transform.position = new Vector3(-10, -11, 0);
diskfactory.FreeDisk(hit.collider.gameObject);
return true;
}
return false;
}
public int Getscore(){
return score;
}
}
- UserGui (用户界面)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UserGUI : MonoBehaviour
{
private IUserAction action;
private bool gamestart = false;
public int HP = 5;
void Start()
{
action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
}
void OnGUI()
{ GUIStyle fontStyle2 = new GUIStyle();
fontStyle2.fontSize = 20;
fontStyle2.normal.textColor= new Color(1, 1, 1);
GUIStyle fontStyle1 = new GUIStyle();
fontStyle1.fontSize = 20;
if(GUI.Button (new Rect (Screen.width-100, Screen.height-100, 65, 65), "Exit")){
Application.Quit();
}
if(gamestart == false){
GUI.Label(new Rect(Screen.width / 2 - 300, 100, 420, 50), "游戏目标:尽可能的射击多的飞碟", fontStyle2);
GUI.Label(new Rect(Screen.width / 2 - 300, 150, 420, 50), "游戏难度会逐渐提升,祝君好运!", fontStyle2);
if (GUI.Button(new Rect(Screen.width / 2 - 40, Screen.height-200, 70, 40), "start")){
action.Startgame();
gamestart = true;
}
}
else{
GUI.Label(new Rect(Screen.width / 2 - 300, 10, 420, 50), "分数:", fontStyle1);
GUI.Label(new Rect(Screen.width / 2 - 250, 10, 420, 50), action.Getscore().ToString(), fontStyle1);
GUI.Label(new Rect(Screen.width / 2 - 300, 40, 420, 50), "剩余血量:", fontStyle1);
GUI.Label(new Rect(Screen.width / 2 - 210, 40, 420, 50), HP.ToString(), fontStyle1);
}
if (Input.GetButtonDown("Fire1")) {
action.Hit();
}
if (HP <= 0){
GUI.Label(new Rect(Screen.width / 2 - 50, 150, 60, 40), "Game Over!", fontStyle1);
action.Stopgame();
if (GUI.Button(new Rect(Screen.width / 2 - 40, Screen.height-200, 70, 40), "Restart")){
action.Restart();
HP = 5;
}
}
}
}
游戏截图
上一篇: unity实现简单巡逻兵
下一篇: SpringCloud学习笔记4