Unity游戏设计之飞碟游戏
编制一个射飞碟游戏。
具体要求如下:
1 假设有一支枪在摄像机位置(0,1,-10),在(0,0,0-10-20)放置三个小球作为距离标记,调整视角直到小球在下中部
2 将鼠标所在平面坐标,转换为子弹(球体)射出的角度方向。子弹使用物理引擎,初速度恒定。(U3d 坐标变换: http://www.cnblogs.com/tekkaman/p/3809409.html )
Vector3 mp = Input.mousePosition; //get Screen Position
print (mp.ToString());
Vector3 mp1 = cam.camera.ScreenToViewportPoint (mp);
mp1.z = 10; //距摄像头 10 位置立面
mp1 = cam.camera.ViewportToWorldPoint (mp1);
print (mp1.ToString());
3 游戏要分多个 round , 飞碟数量每个 round 都是 n 个,但色彩,大小;发射位置,速度,角度,每次发射数量按预定规则变化。
4 用户按空格后,321倒数3秒,飞碟飞出(物理引擎控制),点击鼠标,子弹飞出。飞碟落地,或被击中,则准备下一次射击。
5 以下是一些技术要求:
◦ 子弹仅需要一个,不用时处于 deactive 状态
◦ 飞碟用一个带缓存的工厂生产,template 中放置预制的飞碟对象
◦ 程序类图设计大致如下:
具体实现:
脚本实现子弹射击
脚本挂在在摄像机上
子弹射击的思路:当用户点击鼠标时,从摄像机到鼠标创建一条射线,射线的方向即是子弹发射的方向,子弹采用刚体组件,因此发射子弹只需要给子弹施加一个力。子弹对象只有一个,下一次发射子弹时,必须改变子弹的位置(虽然有了刚体组件不建议修改transform,但也没有其它方法改变子弹位置了吧)。为了不让子弹继承上一次发射的速度,必须将子弹的速度归零重置。
子弹的击中判断:采用射线而不是物理引擎,因为物理引擎在高速物体碰撞时经常不能百分百检测得到。
完成飞碟工厂
创建新的命名空间Com.Mygame,单例类DiskFactory和SceneController都定义其中。飞碟工厂类的目的是管理飞碟实例,同时对外屏蔽飞碟实例的的提取和回收细节,对于需要使用飞碟的其他对象,只能使用工厂类提供的3个函数,分别是getDisk()、getDiskObject()、free()。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Com.Mygame;
namespace Com.Mygame {public class DiskFactory: System.Object {private static DiskFactory _instance;private static List<GameObject> diskList;public GameObject diskPrefab;public static DiskFactory getInstance() { if (_instance == null) { _instance = new DiskFactory(); diskList = new List<GameObject>(); } return _instance; }// 获取可用飞碟id public int getDisk() { for (int i = 0; i < diskList.Count; ++i) { if (!diskList[i].activeInHierarchy) { return i; // 飞碟空闲 } } // 无空闲飞碟,则实例新的飞碟预设diskList.Add(GameObject.Instantiate(diskPrefab) as GameObject); return diskList.Count-1; }// 获取飞碟对象 public GameObject getDiskObject(int id) { if (id >= 0 && id < diskList.Count) { return diskList[id]; } return null; }// 回收飞碟 public void free(int id) { if (id >= 0 && id < diskList.Count) { // 重置飞碟速度 diskList[id].GetComponent<Rigidbody>().velocity = Vector3.zero; // 重置飞碟大小 diskList[id].transform.localScale = diskPrefab.transform.localScale; diskList[id].SetActive(false); } }}
}
public class DiskFactoryBaseCode : MonoBehaviour {public GameObject disk; void Awake () { // 初始化预设对象 DiskFactory.getInstance().diskPrefab = disk; }
}
完成游戏场景
场景类是整个飞碟射击游戏的核心类,主要负责飞碟动作的处理。参考师兄的设计:首先需要倒计时功能,可以通过几个整型变量和布尔变量完成。另外需要飞碟发射功能,通过setting函数保存好飞碟的发射信息,每次倒计时完成后,通过emitDisks获取飞碟对象,并通过发射信息初始化飞碟,再给飞碟一个力就可以发射了。而飞碟的回收在Update里完成,一种是飞碟被击中(飞碟不在场景中)了,需要调用Judge获得分数。另一种是飞碟在场景中,但是掉在地上了,需要调用Judge丢失分数。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Com.Mygame;
public class GameModel : MonoBehaviour {public float countDown = 3f;public float timeToEmit;private bool counting;private bool shooting;public bool isCounting() {return counting;}public bool isShooting() {return shooting;}private List<GameObject> disks = new List<GameObject> ();private List<int> diskIds = new List<int> ();private int diskScale;private Color diskColor;private Vector3 emitPosition;private Vector3 emitDirection;private float emitSpeed;private int emitNumber;private bool emitEnable;private SceneController scene;void Awake() {scene = SceneController.getInstance ();scene.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 prepareToEmitDisk() {if (!counting && !shooting) {timeToEmit = countDown;emitEnable = true;}}void emitDisks() {for (int i = 0; i < emitNumber; i++) {diskIds.Add (DiskFactory.getInstance ().getDisk ());disks.Add (DiskFactory.getInstance ().getDiskObject (diskIds [i]));disks [i].transform.localScale *= diskScale;disks [i].GetComponent<Renderer> ().material.color = diskColor;disks [i].transform.position = new Vector3 (emitPosition.x, emitPosition.y + i, emitPosition.z);disks [i].SetActive (true);disks [i].GetComponent<Rigidbody> ().AddForce (emitDirection * Random.Range (emitSpeed * 5, emitSpeed * 10) / 10, ForceMode.Impulse);}}void freeDisk(int i) {DiskFactory.getInstance ().free (diskIds [i]);disks.RemoveAt (i);diskIds.RemoveAt (i);}void FixedUpdate() {if (timeToEmit > 0) {counting = true;timeToEmit -= Time.deltaTime;} else {counting = false;if (emitEnable) {emitDisks ();emitEnable = false;shooting = true;}}}// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {for (int i = 0; i < disks.Count; i++) {if (!disks [i].activeInHierarchy) {scene.getJudge ().scoreADisk ();freeDisk (i);} else if (disks [i].transform.position.y < 0) {scene.getJudge ().failADisk ();freeDisk (i);}}if (disks.Count == 0) {shooting = false;}}
}
场景控制器
场景控制类主要实现接口定义和保存注入对象。另外它有两个私有变量round和point,分别记录游戏正在进行的回合,以及玩家目前的得分。
using UnityEngine;
using System.Collections;
using Com.Mygame;
namespace Com.Mygame {// Com.Mygame内添加public interface IUserInterface {void emitDisk();}public interface IQueryStatus {bool isCounting();bool isShooting();int getRound();int getPoint();int getEmitTime();}public class SceneController : System.Object, IQueryStatus, IUserInterface {private static SceneController _instance;private GameModel _gameModel;private SceneControllerBaseCode _baseCode;private int _round;private int _point;public static SceneController getInstance() {if (_instance == null) {_instance = new SceneController ();}return _instance;}public void setSceneControllerBaseCode (SceneControllerBaseCode obj) {_baseCode = obj;} internal SceneControllerBaseCode getSceneControllerBC() {return _baseCode;} public void setGameModel(GameModel obj) {_gameModel = obj;}// 当前程序或派生类可用internal GameModel getGameModel() {return _gameModel;}public void emitDisk() {_gameModel.prepareToEmitDisk ();}public bool isCounting() {return _gameModel.isCounting ();}public bool isShooting() {return _gameModel.isShooting ();}public int getRound() {return _round;}public int getPoint() {return _point;}public int getEmitTime() {return (int)_gameModel.timeToEmit + 1;}public void setPoint(int point) {_point = point;}public void nextRound() {_point = 0;}}
}
public class SceneControllerBaseCode : MonoBehaviour { private Color color; private Vector3 emitPos; private Vector3 emitDir; private float speed; void Awake() { SceneController.getInstance().setSceneControllerBaseCode(this); } void Start() {color = Color.green; emitPos = new Vector3(-2.5f, 0.2f, -5f); emitDir = new Vector3(24.5f, 40.0f, 67f); speed = 4;SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 1); }
}
完善UserInterface
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using Com.Mygame;
public class UserInterface : MonoBehaviour {public Text mainText;public Text scoreText;public Text roundText;private int round;public GameObject bullet;public ParticleSystem explosion;public float fireRate = .25f;public float speed = 500f;private float nextFireTime;private IUserInterface userInt;private IQueryStatus queryInt;// Use this for initializationvoid Start () {bullet = GameObject.Instantiate (bullet) as GameObject;explosion = GameObject.Instantiate (explosion) as ParticleSystem;userInt = SceneController.getInstance () as IUserInterface;queryInt = SceneController.getInstance () as IQueryStatus;}// Update is called once per framevoid Update () {if (queryInt.isCounting ()) {mainText.text = ((int)queryInt.getEmitTime ()).ToString ();} else {if (Input.GetKeyDown (KeyCode.Space)) {userInt.emitDisk ();}if (queryInt.isShooting ()) {mainText.text = " ";} else {mainText.text = "Press space";}if (queryInt.isShooting() && Input.GetMouseButtonDown (0) && Time.time > nextFireTime) {nextFireTime = Time.time + fireRate;Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);bullet.GetComponent<Rigidbody> ().velocity = Vector3.zero;bullet.transform.position = transform.position;bullet.GetComponent<Rigidbody> ().AddForce (ray.direction * speed, ForceMode.Impulse);RaycastHit hit;if (Physics.Raycast (ray, out hit) && hit.collider.gameObject.tag == "Disk") {explosion.transform.position = hit.collider.gameObject.transform.position;explosion.GetComponent<Renderer> ().material.color = hit.collider.gameObject.GetComponent<Renderer> ().material.color;explosion.Play ();hit.collider.gameObject.SetActive (false);}}}roundText.text = " Round: " + queryInt.getRound ().ToString ();scoreText.text = " Score: " + queryInt.getPoint ().ToString ();if (round != queryInt.getRound ()) {round = queryInt.getRound ();mainText.text = "Round: " + round.ToString() + "!";}}
}
补充游戏规则–Judge计分系统
游戏规则单独作为一个类,有利于日后修改。这里需要处理的规则无非就两个,得分和失分。另外,得分需要判断是否能晋级下一关。能就调用接口函数nextRound()。
using UnityEngine;
using System.Collections;
using Com.Mygame; public class Judge : MonoBehaviour { public int oneDiskScore = 10; public int oneDiskFail = 10; public int disksToWin = 4; private SceneController scene; void Awake() { scene = SceneController.getInstance(); scene.setJudge(this); } void Start() { scene.nextRound(); // 默认开始第一关 } // 击中飞碟得分 public void scoreADisk() { scene.setPoint(scene.getPoint() + oneDiskScore); if (scene.getPoint() == disksToWin*oneDiskScore) { scene.nextRound(); } } // 掉落飞碟失分 public void failADisk() { scene.setPoint(scene.getPoint() - oneDiskFail); }
}
在场景控制器中添加相应裁判的代码
// Com.Mygame内添加public interface IjudgeEvent {void nextRound();void setPoint(int point);}
// 类内部添加并且类继承IjudgeEventprivate Judge _judge; public void setJudge(Judge obj) { _judge = obj; } internal Judge getJudge() { return _judge; }
在GameModel中调用裁判计分功能
void Update () {for (int i = 0; i < disks.Count; i++) {if (!disks [i].activeInHierarchy) {scene.getJudge ().scoreADisk ();freeDisk (i);} else if (disks [i].transform.position.y < 0) {scene.getJudge ().failADisk ();freeDisk (i);}}if (disks.Count == 0) {shooting = false;}}
设置关卡
在SceneControllerBaseCode中添加关卡信息,通过添加loadRoundData来完成每个关卡对游戏对象属性的设置。
public void loadRoundData(int round) { switch(round) { case 1: // 第一关 color = Color.green; emitPos = new Vector3(-2.5f, 0.2f, -5f); emitDir = new Vector3(24.5f, 40.0f, 67f); speed = 4; SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 1); break; case 2: // 第二关 color = Color.red; emitPos = new Vector3(2.5f, 0.2f, -5f); emitDir = new Vector3(-24.5f, 35.0f, 67f); speed = 4; SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 2); break;case 3: // 第二关 color = Color.yellow; emitPos = new Vector3(2.5f, 0.2f, -5f); emitDir = new Vector3(-24.5f, 35.0f, 67f); speed = 4; SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 3); break; } }
游戏效果:
参考链接:
http://blog.csdn.net/simba_scorpio/article/details/51051241
转载于:https://www.cnblogs.com/alala713/p/6892067.html
Unity游戏设计之飞碟游戏相关推荐
- unity 实现简易打飞碟游戏
unity 实现简易打飞碟游戏 一.简介 游戏共有5个回合,每个回合中会有随机产生的飞碟飞过屏幕,玩家需要做的事情就是用鼠标尽量快和多地点击飞碟. 每个飞碟对应一定的分数,目前的设置是: [红色飞碟 ...
- Unity 3D-learning 简单打飞碟游戏
一.编写一个简单的打飞碟游戏 游戏内容要求: 游戏有 n 个 round,每个 round 都包括10 次 trial: 每个 trial 的飞碟的色彩.大小.发射位置.速度.角度.同时出现的个数都可 ...
- 游戏设计:何为游戏框架
当你问一名<魔兽世界>的玩家,"为什么现在WOW的主线剧情里,始终会有一个或者多个NPC,跟着玩家一起做任务?"时,大部分玩家的回答都是相同的--因为这样会很有趣.我相 ...
- Android 游戏设计教程:游戏元素和工具
转自:http://www.oschina.net/question/28_57188 作者:Richard A. Rogers (本节内容选自<Learning Android Game Pr ...
- 2d游戏设计,pygame 游戏开发
此次作业是使用pygame模块自主设计一款2D游戏. 此次制作的游戏灵感来源于jumpKing,这是一款出售于steam的高技术游戏. 我们选择了他们的操作方式: 需A.D键来控制人物的移动,以及空格 ...
- 【游戏设计笔记】游戏开发团队
I think you'll find the biggest critics of WoW to be the WoW development team. --Jeffrey Kaplan(a l ...
- 【游戏设计笔记】游戏设计第一堂课
I cannot give you the formula for success,but I can give you the formula for failure-which is:Try to ...
- Android游戏设计教程:游戏元素和工具
(本节内容选自<Learning Android Game Programming>这本书,主要分享针对Android设备开发游戏的基本原则和方法.) 创造优秀手机游戏不仅需要编写某些相当 ...
- python游戏设计_python小游戏设计入门1-了解游戏
从小到大玩过很多的游戏,在我小时候,能玩游戏的地方不多,那时玩游戏都是偷摸玩的,只要是个游戏就觉得非常有趣,比较经典的有魂斗罗,拳皇,超级玛丽,贪吃蛇,俄罗斯方块等游戏:发展到现在,玩游戏已经成为生活 ...
最新文章
- 问题集锦(21-25)
- nodejs全局变量第一次没赋值要第二次才有是为什么_【NodeJS】async 和 await 的本质...
- ASP+页缓存OutputCache Duration用法
- 遇到联邦计算数据碰撞难题怎么办?不妨试一试PSI
- 长沙试水数字人民币:线下支持数字人民币支付的商家已达3404个
- 对最大熵模型为什么要最大化熵的一点理解
- 用Visual Studio 2005/2008提取EXE文件中的资源[图片|htm|光标文件]
- springMVC 拦截器 过滤器 区别 执行顺序
- ckeditor5富文本数学化学方程式
- 冲突声明(conflicting declaration)解决
- 智能驾仓上蓝牙应用案例
- 东方财富:资金流向表爬虫
- 那些年找工作入过的坑! 避雷!!!!
- 华为荣耀play使用WiFi调试Android的坑
- 来,带你实现基于网络通信QQ聊天室-----QQ有这么强!!!
- 可解释的机器学习,用于科学的见解和发现(Explainable Machine Learning for Scientific Insights and Discoveries)
- ndnSIM学习(四)——examples之ndn-simple.cpp超详细剖析
- java版怎么安装mod_java – 如何在Mac OS X上安装mod_jk
- 【定制开发】【M8】推荐一款强大的磁盘清理小工具,专治磁盘爆满(不到1M)
- ios12升级, App应用崩溃闪退