admin管理员组文章数量:1665711
游戏规则:游戏有三个关卡,每个关卡有四次发射机会,每次发射的飞碟大小颜色速度方向位置都不同,有50分初始分,每打中一个加10分,掉落一个在地减10分,第一二关都是90分过关,第三关70分过关,第一关每次发射一个飞碟,第二关两个,第三关三个。空格发射飞碟,鼠标左键射击飞碟。如图(虽然UI还没怎么优化过,将就一下了,还有没有录第三关也是为了让gif短一点,因为上传的gif有内存限制):
下面进入正题,怎么制作呢?
回收与再利用:首先这种涉及到类似于子弹啊,雨滴等物体的游戏,这种资源最好能回收与再利用,这样就可以节约资源,省的一直创建新对象,别看创建一个对象对游戏影响不怎么大,在一些大型游戏中这是很重要的思想的,因为一个场景往往涉及到成千上万的这种小物体,如果每一个都要创建一遍才能用,会导致卡顿,速度跟不上。
工厂模式:物体的生成都由一个工厂管理,需要什么只需要跟工厂说出你要什么,它会自己实现生成给你,里面如何生成不用你操心,实际上也有抽象的意思。就像这次的游戏,我数据设置好了,预设也传给了工厂,所以在需要加载飞碟是,只用调用一个getDisk()就可以了,不需要操心怎么生成。当然回收与再利用也是在工厂中实现的,也就是回收仓库也是在工厂里。
射线碰撞:用鼠标点击屏幕,如何获取我所点到的物体以及我的鼠标坐标呢?就是用从摄像机中发射出射线,然后鼠标点的位置作为方向,这样就可以选中第一个被射击中的物体,同时也可以记录出鼠标所点位置。
下面是这游戏制作代码的架构图:
SceneController类:负责把各个类之间对接,作为内部设置与用户界面的连接。
GameModel类:管理飞碟的基础数据。
RoundController类:管理关卡数据,每一个关卡对应需要不同的飞碟数据,在这里会给飞碟根据不通关卡加载不同属性。
Recorder类:管理记录加分减分。
Userface类:管理用户界面以及界面逻辑。
DiskFactory类:利用传进来的飞碟预设根据需要生产飞碟存于仓库,回收与再利用飞碟。
下面是代码:
工厂类(看注释应该就能看懂):
public class DiskFactory : System.Object
{
private static DiskFactory _instance;
private GameObject prefabs_disk;
public static List<GameObject> used = new List<GameObject>();
public static List<GameObject> unused = new List<GameObject>();
public static DiskFactory getInstance() //单例化工厂
{
if (_instance == null)
{
_instance = new DiskFactory();
}
return _instance;
}
public void SetObject(GameObject x) //把预设传入工厂
{
prefabs_disk = x;
}
public GameObject getDisk() //提供从工厂获取飞碟的借口
{
GameObject t;
if (unused.Count == 0)
t = GameObject.Instantiate(prefabs_disk);
else
{
t = unused[0];
unused.Remove(t);
}
used.Add(t);
return t;
}
public void free(GameObject t) //提供从外界回收飞碟到工厂的接口
{
t.GetComponent<Rigidbody>().velocity = Vector3.zero;
t.transform.localScale = prefabs_disk.transform.localScale;
t.SetActive(false);
used.Remove(t);
unused.Add(t);
}
}
接下来是SceneController类,它的工作实际上就是把各个类的功能合并起来,需要什么功能就它就调用某个类的功能来实现:
public class SceneController : System.Object, IQuery, IUserInterface
{
private static SceneController _instance;
private RoundController _RC;
private GameModel _gameModel;
private Recorder _Recorder;
public static SceneController getInstance() //单例化
{
if (_instance == null)
{
_instance = new SceneController();
}
return _instance;
}
public void setdisk(GameObject t) //把飞碟预设传入工厂
{
DiskFactory.getInstance().SetObject(t);
}
public int getRound()
{
return _RC.round;
}
public int getScore()
{
return _Recorder.score;
}
public void setGameModel(GameModel obj) { _gameModel = obj; }
internal GameModel getGameModel() { return _gameModel; }
public void setRecorder(Recorder obj) { _Recorder = obj; }
internal Recorder getRecorder() { return _Recorder; }
public void setRoundController(RoundController obj) { _RC = obj; }
internal RoundController getRoundController() { return _RC; }
public void emitDisk() { _gameModel.emitReady(); }
public bool isShooting() { return _gameModel.shooting; }
public void nextRound(int i) //进入下一关卡
{
if (i == 1 || i == 2)
{
_Recorder.reset();
_RC.round++;
_RC.loadRoundData();
}
else if (i == 0) _RC.round = 0;
}
}
两个用户与SceneController的接口(在SceneController实现):
public interface IUserInterface //Scenecontroller与用户界面的接口
{
void emitDisk();
void setdisk(GameObject t);
}
public interface IQuery //Scenecontroller与用户界面的接口
{
bool isShooting();
int getRound();
int getScore();
}
以上三个部分我都放在一个自定义的namespace里面(name_game);
Userface用户界面实现UI以及提供用户接口逻辑:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using name_game;
public class UserInterface : MonoBehaviour {
public int changes = 4; //发射机会
public Text roundText; //显示目前关卡数
public Text scoreText; //显示目前分数
public Text mainText; //主显示板,显示规则以及提示
public Text changeText; //显示目前剩余发射机会
public Camera ca;
private int states = 0; //0为初始界面,1为规则界面,2为游戏中
private IUserInterface userInt; // 用户接口
private IQuery queryInt; // 查询接口
public GameObject disktem;
public ParticleSystem _exposion; //爆炸粒子
void Start()
{
userInt = SceneController.getInstance() as IUserInterface;
queryInt = SceneController.getInstance() as IQuery;
_exposion = GameObject.Instantiate(_exposion) as ParticleSystem;
changes = 4;
states = 0;
userInt.setdisk(disktem);
mainText.text = "欢迎来到飞碟世界,请按空格进入游戏";
}
void Update()
{
if ((queryInt.getRound() == 1 || queryInt.getRound() == 2 )&& changes == 0 && !queryInt.isShooting()) //判断一二关的结果
{
if (queryInt.getScore() >= 90)
{
mainText.text = "You win!(空格进入下一关)";
if (Input.GetKeyDown("space"))
{
states = 1;
changes = 4;
SceneController.getInstance().nextRound(1);
}
}
else
{
mainText.text = "You lose!(空格重新开始)";
if (Input.GetKeyDown("space"))
{
states = 0;
changes = 4;
SceneController.getInstance().nextRound(0);
}
}
}
if (queryInt.getRound() == 3 && changes == 0 && !queryInt.isShooting()) //判断第三关的结果
{
if (queryInt.getScore() >= 70) mainText.text = "You win!You have passed all arounds(空格重新开始游戏)";
else mainText.text = "You lose!(空格重新开始)";
if (Input.GetKeyDown("space"))
{
states = 0;
changes = 4;
SceneController.getInstance().nextRound(0);
}
}
changeText.text = "Changes: " + changes.ToString();
if (changes>0&&Input.GetKeyDown("space")) {
if(states==0)
{
states = 1;
if(queryInt.getRound()==0) SceneController.getInstance().nextRound(1);
mainText.text = "你有50初始分,每一关有四次发射机会,每次发射射出的飞碟数量以及速度还有大小颜色都不同,每一轮发射中每一个飞碟掉落在地都会扣10分,打中一个加10分,第一二关90分以上过关第三关70分以上过关,空格发射飞碟,鼠标左键射击,请按空格进入第一关";
} else if(states==1)
{
states = 2;
mainText.text = "";
} else if(states==2) //这个状态下才能发射
{
if ((!queryInt.isShooting())&&changes!=0)
{
userInt.emitDisk();
changes--;
}
}
}
if(queryInt.isShooting()&&Input.GetMouseButtonDown(0)) //判断是否射击中目标,并且实现爆炸粒子效果
{
Ray ray = ca.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit) && hit.collider.gameObject.tag == "disk")
{
_exposion.transform.position = hit.collider.gameObject.transform.position;
_exposion.GetComponent<Renderer>().material.color = hit.collider.gameObject.GetComponent<Renderer>().material.color;
_exposion.Play();
hit.collider.gameObject.SetActive(false);
}
}
roundText.text = "Round: " + queryInt.getRound().ToString();
scoreText.text = "Score: " + queryInt.getScore().ToString();
}
}
Recorder类:
public class Recorder : MonoBehaviour {
public int score = 50; //记录当前所得分数
void Awake () {
SceneController.getInstance().setRecorder(this);
}
public void scoreADisk() //得分
{
score += 10;
}
public void reset() //重置
{
score = 50;
}
public void failADisk() // 失分
{
score -= 10;
}
}
RoundController类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using name_game;
public class RoundController : MonoBehaviour {
private Color color;
private Vector3 emitPos;
private Vector3 emitDir;
private float speed;
public int round = 0;
void Awake()
{
SceneController.getInstance().setRoundController(this);
}
void Start () {
emitPos = new Vector3(0f, 5f, -6f);
emitDir = new Vector3(10f, 15f, 70f);
speed = 15;
}
public void loadRoundData() //加载每一关卡飞碟数据
{
if(round==1)
{
color = Color.black;
SceneController.getInstance().getGameModel().setting(3, color, emitPos, emitDir.normalized, speed, 1);
} else
if(round==2)
{
color = Color.red;
SceneController.getInstance().getGameModel().setting(2, color, emitPos, emitDir.normalized, speed, 2);
} else if(round==3)
{
color = Color.green;
SceneController.getInstance().getGameModel().setting(2, color, emitPos, emitDir.normalized, speed, 3);
}
}
}
GameModel类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using name_game;
public class GameModel : MonoBehaviour
{
public bool shooting=false; //标记飞碟飞行期间的状态
private int diskScale; //飞碟大小
private Color diskColor; //飞碟颜色
private Vector3 emitPosition; // 发射位置
private Vector3 emitDirection; // 发射方向
private float emitSpeed = 0; // 发射速度
private int emitNumber; // 发射数量
private bool emitEnable;
private List<GameObject> disksToEmit = new List<GameObject>();
void Awake()
{
SceneController.getInstance().setGameModel(this);
}
public void setting(int scale, Color color, Vector3 emitPos, Vector3 emitDir, float speed, int num)
{
diskScale = scale;
diskColor = color;
emitPosition = emitPos;
emitDirection = emitDir;
emitSpeed = speed;
emitNumber = num;
}
public void emitReady() //判断是否可以发射
{
if (!shooting)
{
emitEnable = true;
}
}
void emitDisks() //从工厂生产或者取出飞碟并且装入实现发射逻辑
{
for (int i = 0; i < emitNumber; ++i) //每一个飞碟发射前都随机一次方向和发射位置
{
disksToEmit.Add(DiskFactory.getInstance().getDisk());
disksToEmit[i].transform.localScale *= diskScale;
disksToEmit[i].GetComponent<Renderer>().material.color = diskColor;
disksToEmit[i].transform.position = new Vector3(Random.Range(emitPosition.x-1f, emitPosition.x+1f), emitPosition.y, emitPosition.z);
disksToEmit[i].SetActive(true);
emitDirection = new Vector3(Random.Range(emitDirection.x-0.3f, emitDirection.x + 0.3f), emitDirection.y, emitDirection.z);
disksToEmit[i].GetComponent<Rigidbody>().velocity = emitDirection * Random.Range(emitSpeed, emitSpeed+5f);
}
}
void freeDisk(GameObject adisk) //调用工厂来回收飞碟
{
DiskFactory.getInstance().free(adisk);
disksToEmit.Remove(adisk);
}
void FixedUpdate()
{
if (emitEnable)
{
emitDisks(); // 发射飞碟
emitEnable = false;
shooting = true;
}
}
void Update()
{
for (int i = 0; i < disksToEmit.Count; ++i) //实现判断是否射击中
{
if (!disksToEmit[i].activeInHierarchy)
{ // 飞碟不在场景中
SceneController.getInstance().getRecorder().scoreADisk(); // 得分
freeDisk(disksToEmit[i]);
}
else if (disksToEmit[i].transform.position.y < 0)
{ // 飞碟在场景中但落地
SceneController.getInstance().getRecorder().failADisk(); // 失分
freeDisk(disksToEmit[i]);
}
}
if (disksToEmit.Count == 0)
{
shooting = false;
}
}
}
还有一点就是要给飞碟的预设就如刚体组件,在Add Component->physics->Rigidbody就可以了
Text的话可以create一个Canvas,在里面创建并且调整各个提示框的位置以及大小
粒子系统就直接create->Particle System创建,然后样式再根据自己需要调一下。这次的游戏就大功告成了~~~
版权声明:本文标题:Unity3D学习:射击小游戏——飞碟世界 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1730031538a1220019.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论