编制一个射飞碟游戏。

具体要求如下:

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游戏设计之飞碟游戏相关推荐

  1. unity 实现简易打飞碟游戏

    unity 实现简易打飞碟游戏 一.简介 游戏共有5个回合,每个回合中会有随机产生的飞碟飞过屏幕,玩家需要做的事情就是用鼠标尽量快和多地点击飞碟. 每个飞碟对应一定的分数,目前的设置是: [红色飞碟 ...

  2. Unity 3D-learning 简单打飞碟游戏

    一.编写一个简单的打飞碟游戏 游戏内容要求: 游戏有 n 个 round,每个 round 都包括10 次 trial: 每个 trial 的飞碟的色彩.大小.发射位置.速度.角度.同时出现的个数都可 ...

  3. 游戏设计:何为游戏框架

    当你问一名<魔兽世界>的玩家,"为什么现在WOW的主线剧情里,始终会有一个或者多个NPC,跟着玩家一起做任务?"时,大部分玩家的回答都是相同的--因为这样会很有趣.我相 ...

  4. Android 游戏设计教程:游戏元素和工具

    转自:http://www.oschina.net/question/28_57188 作者:Richard A. Rogers (本节内容选自<Learning Android Game Pr ...

  5. 2d游戏设计,pygame 游戏开发

    此次作业是使用pygame模块自主设计一款2D游戏. 此次制作的游戏灵感来源于jumpKing,这是一款出售于steam的高技术游戏. 我们选择了他们的操作方式: 需A.D键来控制人物的移动,以及空格 ...

  6. 【游戏设计笔记】游戏开发团队

    I think you'll find the biggest critics of  WoW to be the WoW development team. --Jeffrey Kaplan(a l ...

  7. 【游戏设计笔记】游戏设计第一堂课

    I cannot give you the formula for success,but I can give you the formula for failure-which is:Try to ...

  8. Android游戏设计教程:游戏元素和工具

    (本节内容选自<Learning Android Game Programming>这本书,主要分享针对Android设备开发游戏的基本原则和方法.) 创造优秀手机游戏不仅需要编写某些相当 ...

  9. python游戏设计_python小游戏设计入门1-了解游戏

    从小到大玩过很多的游戏,在我小时候,能玩游戏的地方不多,那时玩游戏都是偷摸玩的,只要是个游戏就觉得非常有趣,比较经典的有魂斗罗,拳皇,超级玛丽,贪吃蛇,俄罗斯方块等游戏:发展到现在,玩游戏已经成为生活 ...

最新文章

  1. 问题集锦(21-25)
  2. nodejs全局变量第一次没赋值要第二次才有是为什么_【NodeJS】async 和 await 的本质...
  3. ASP+页缓存OutputCache Duration用法
  4. 遇到联邦计算数据碰撞难题怎么办?不妨试一试PSI
  5. 长沙试水数字人民币:线下支持数字人民币支付的商家已达3404个
  6. 对最大熵模型为什么要最大化熵的一点理解
  7. 用Visual Studio 2005/2008提取EXE文件中的资源[图片|htm|光标文件]
  8. springMVC 拦截器 过滤器 区别 执行顺序
  9. ckeditor5富文本数学化学方程式
  10. 冲突声明(conflicting declaration)解决
  11. 智能驾仓上蓝牙应用案例
  12. 东方财富:资金流向表爬虫
  13. 那些年找工作入过的坑! 避雷!!!!
  14. 华为荣耀play使用WiFi调试Android的坑
  15. 来,带你实现基于网络通信QQ聊天室-----QQ有这么强!!!
  16. 可解释的机器学习,用于科学的见解和发现(Explainable Machine Learning for Scientific Insights and Discoveries)
  17. ndnSIM学习(四)——examples之ndn-simple.cpp超详细剖析
  18. java版怎么安装mod_java – 如何在Mac OS X上安装mod_jk
  19. 【定制开发】【M8】推荐一款强大的磁盘清理小工具,专治磁盘爆满(不到1M)
  20. ios12升级, App应用崩溃闪退

热门文章

  1. win10系统administrator账户密码忘记
  2. 织梦编辑器加HTML视频显示很小,织梦教程:去掉编辑器自动加div的方法即大小字情况...
  3. Web应用——驾培管理系统之个人管理(作者:小圣)
  4. 常见的穿透技术有哪些?
  5. 用计算机来谈生僻字,语义搜索及框计算:从百度查生僻字谈起
  6. zookeeper集群安装和配置解读 - 雨中散步撒哈拉
  7. Windows下 iCloud Drive 更改缓存文件夹位置
  8. 神经网络自我认知模型
  9. Flutter:常见编码问题及解决
  10. The ES9038Q2M SABRE DAC