Unity3D学习:射击小游戏——飞碟世界
游戏规则:游戏有三个关卡,每个关卡有四次发射机会,每次发射的飞碟大小颜色速度方向位置都不同,有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学习:射击小游戏——飞碟世界相关推荐
- Unity3D 2D射击小游戏瞄准线的实现
Unity3D 2D射击小游戏瞄准线的实现 0. 前言 本片文章是对 https://www.bilibili.com/video/bv1sY411V7tx 视频中瞄准线是如何实现的一个讲解教程,欢迎 ...
- 基于Unity3D的AR小游戏开发【100011412】
本科毕业设计(论文) GRADUATION DESIGN(THESIS) 基于 Unity3D 的增强现实游戏程序 摘要 增强现实(AR)作为一项新兴技术近年来被越来越多的人群所获知,AR 也渐渐走进 ...
- 坦克射击小游戏(后续将会升级功能)
坦克射击小游戏(后续将会升级功能) 总体框架 关键技术 未来版本 我的Github 游戏源码在我的Github,感兴趣的的帮忙给颗星呀,并加入我们吧~ 这是我跟着b站韩顺平老师学的一个很锻炼基础的小项 ...
- Unity3D休闲射击类游戏《Survival Shooter》完整源码
Unity3D休闲射击类游戏<Survival Shooter>完整源码分享给大家学习,这个对于那些想要制作u3d射击类游戏有很大帮助. 运行环境是:Unity5.3.1 下载地址: ht ...
- Java面向对象(6) —— 射击小游戏
这里做一个射击小游戏,作为对前面Java学习的一个小结. 1.设计 2.知识点总结 主要是在主程序ShootGame.java中引入了一些新知识,swing画图.定时器.鼠标事件处理.其他程序则主要体 ...
- Android 自定义View实现炮弹射击小游戏
先看效果图: 这是一个比较简易的射击小游戏,后期可以将圆球,炮筒用其它图片来替换,应该可以变得好看一些.我实现这个效果,主要是为了学习和巩固自定义View的一些知识点.下面我来讲述一下本游戏的设计思路 ...
- 射击小游戏c语言实验报告,C++实现简单射击小游戏
使用c++制作简单的横板射击小游戏,供大家参考,具体内容如下 #include #include #include class Bullet; class Tank; class E_Bullet; ...
- 基于pygame的射击小游戏制作(一)让飞船动起来
基于pygame的射击小游戏制作(一)让飞船动起来 一.文件结构 alien_invasion.py 是整个系统的主文件,用来创建游戏中的一系列对象,ai_settings存储设置.screen存储显 ...
- Python学习—2048小游戏等4个小练习
Python学习-2048小游戏等4个小练习 转载于:https://www.cnblogs.com/ChangAn223/p/10627777.html
最新文章
- GUI(概述和布局)
- factorybean 声明_阿里面试题:BeanFactory与FactoryBean的区别
- 【.NET Core项目实战-统一认证平台】第七章 网关篇-自定义客户端限流
- 性价比高台式计算机,2021年台式电脑什么牌子好,性价比高?
- python可以做计量分析吗_技术分享 - python数据分析(2)——数据特征分析(上)...
- IDEA 启动、编译、clean、安装maven等、报错Process terminated
- java子类和父类实例_java中父类与子类之间的转换示例
- U盘文件系统无法识别,数据怎么恢复?
- 【LeetCode】79-单词搜索
- (17)FPGA速度和面积互换原则
- VS2010下如何改变Button颜色、背景、边框
- python英文版怎么改成中文版_“PyQt的Qt Designer菜单是中文版的,怎么改成英文的“python的qt designer教程...
- 电脑计算机无法安3.5,Windows无法安装以下功能:.NET Framework 3.5(包括 .NET 2.0 和 3.0)解决方法...
- SQL Server触发器更新原理
- 浅谈ThreadPoolExecutor线程池底层源码
- 国企计算机岗位职称申请,在国企工作如何评定职称
- 图片转ascii字符画C语言,将图片转为ASCII字符画
- Android中的传感器之---光线传感器
- 学以致用——微博文章内容统计分析之一(Excel+GraphLab)
- python:实现丢番图方程算法(附完整源码)
热门文章
- Quartus||仿真图设计
- 删除你的所有计算机文件的英文,删除Download和DataStore文件夹中的所有文件
- 试题 算法训练 生活大爆炸版石头剪刀布
- updating java index_myeclipse右下角的updating indexes 是什么意思?
- 小米note2 支付宝指纹支付 -10008
- 使用思博伦Avalanche进行HTTP双向吞吐量测试 _双极未来
- java ant配置环境变量
- jsp文字上下居中显示_怎么把jsp的文字居中
- 【定位不准的烦心事系列】第1篇:谈谈卫星定位的位置干扰
- mysql中的dual