Unity笔记-打飞碟游戏
2017-05-08 01:57
507 查看
目的:创建一个打飞碟游戏(简陋)
游戏要求:
游戏要分多个 round , 飞碟数量每个 round 都是 n 个,但色彩,大小;发射位置,速度,角度,每次发射数量可以变化。
游戏过程中,仅能创建 n 个飞碟, 且不容许初始化阶段生成任何飞碟。 飞碟线路计算请使用 mathf 类。 向下加速度 a 是常数。 飞碟被用户击中,则回收。并按你定义的规则计算分数。飞碟落地(y < c),则自动回收。
第一步需要做的是先把一些万年不变的代码写出来。
第二步:回到Unity界面,新建一个圆柱体,命名为Disk,Disk的厚度调整到你认为它看起来像个碟为止,而半径大小则不着急,因为飞碟大小是变化的。
鉴于我是做完才写这个博客的,懒得重新截图,就不放图了
然后在上面新增一个脚本DiskData.cs,并将它做成预制,然后将它从场景中删除。
第三步,需要了解:脚本这个东西可以跟其他Component一样被实例化,实例化之后会有对应的GameObject产生。因此为了记录飞碟的属性,我们新建一个脚本DiskData.cs来记录飞碟的各种属性
Ruler的作用在之后会提到。
在第四步开始前,一定要记住DiskData都挂着一个GameObject,并且可以通过diskdata.gameObject来访问。搞清楚这一点就不会在后面为了选择GameObject还是DiskData作为函数输入输出而烦恼了。
第四步:建立工厂模式
课件给出的伪代码
于是我的代码和他几乎一模一样
Ruler的作用就显现出来了:如果我们要生产一个飞碟,我们需要知道它的所有属性值,这些属性值由场景提供。如果没有Ruler类,那么getDisk的原型是这样的:
冗长不美观是一个问题,但更严重的是,如果我想增加或者删除飞碟的属性,那么我就要修改getDisk的参数列表,那么会非常麻烦而且容易出错。
第五步:设置Ruler的目的还有另外一个就是方便对disk的属性进行修改
联系到前面DiskFactory的代码:
这样就能轻松修改属性。
第六步:运动轨迹
这里的课程还没有教到刚体,因此用坐标变换来实现飞碟的运动。但其实利用刚体感觉更麻烦,而且unity那个施加力的动作至今我都觉得很迷。
我们使用一个方向向量来代表飞碟的方向。这个方向向量是一个单位向量。那么根据相似性,我们可以知道飞碟的初始速度的三个分量分别是初始速度的值乘上方向向量的对应分量的值,例如:
如果方向向量为(x,y,z),速度为a,那么x方向速度为a*x,以此类推。
然后联系到update的机制,update是不断更新的,所以我们可以用现在飞碟的状态,来算出下一时刻飞碟的状态。
在DiskData中增加三个变量:
ruler的set修改为:
然后在场景内增加一个runDisk的函数:
上面代码都是简单的运动学公式。
第七步:
程序的所有代码都在下面的附件全部列出来了。现在将sceneController和DiskFactory挂载在一个空物体上,然后将刚才产生的预制,拖进DiskFactory的inspector标签里的prefab变量。就可以运行了。
你可能会发现里面还有很多代码我没说明,其实这些代码并不是关键,要么是它看起来就很容易理解(例如那个单例模式),要么就是有其它方法代替,例如那个ScoreController,其实完全可以不用。
后记:
这个打飞碟游戏其实还不完善。
- 分数直接print在console里
- round升级没有任何效果
附:所有代码
SSDirector.cs:
Singleton.cs:(这是单例模式)
DiskData.cs:
DiskFactory.cs:
SceneController.cs:
游戏要求:
游戏要分多个 round , 飞碟数量每个 round 都是 n 个,但色彩,大小;发射位置,速度,角度,每次发射数量可以变化。
游戏过程中,仅能创建 n 个飞碟, 且不容许初始化阶段生成任何飞碟。 飞碟线路计算请使用 mathf 类。 向下加速度 a 是常数。 飞碟被用户击中,则回收。并按你定义的规则计算分数。飞碟落地(y < c),则自动回收。
第一步需要做的是先把一些万年不变的代码写出来。
// SSDirector.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class SSDirector : Object { private static SSDirector _instance; public SceneController currentSceneController { get; set; } public bool running { get; set; } public static SSDirector getInstance() { if (_instance == null) { _instance = new SSDirector(); } return _instance; } public int getFPS() { return Application.targetFrameRate; } public void setFPS(int fps) { Application.targetFrameRate = fps; } public void NextScene() { } }
// SceneController.cs public class SceneController : MonoBehaviour { void Awake() { SSDirector director = SSDirector.getInstance(); director.setFPS(60); director.currentSceneController = this; director.currentSceneController.LoadResources(); } public void LoadResources() { } }
第二步:回到Unity界面,新建一个圆柱体,命名为Disk,Disk的厚度调整到你认为它看起来像个碟为止,而半径大小则不着急,因为飞碟大小是变化的。
鉴于我是做完才写这个博客的,懒得重新截图,就不放图了
然后在上面新增一个脚本DiskData.cs,并将它做成预制,然后将它从场景中删除。
第三步,需要了解:脚本这个东西可以跟其他Component一样被实例化,实例化之后会有对应的GameObject产生。因此为了记录飞碟的属性,我们新建一个脚本DiskData.cs来记录飞碟的各种属性
// DiskData.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class Ruler { public Color color; public float size; public Vector3 position; public float speed; public Vector3 direction; // 单位向量 public Ruler(Color color, float size, Vector3 position, float speed, Vector3 direction) { this.color = color; this.size = size; this.position = position; this.speed = speed; this.direction = direction; } } public class DiskData : MonoBehaviour { public Ruler _ruler; }
Ruler的作用在之后会提到。
在第四步开始前,一定要记住DiskData都挂着一个GameObject,并且可以通过diskdata.gameObject来访问。搞清楚这一点就不会在后面为了选择GameObject还是DiskData作为函数输入输出而烦恼了。
第四步:建立工厂模式
课件给出的伪代码
getDisk(ruler) IF (free list has disk) THEN a_disk = remove one from list ELSE a_disk = clone from Prefabs ENDIF Set DiskData of a_disk with the ruler Add a_disk to used list Return a_disk FreeDisk(disk) Find disk in used list IF (not found) THEN THROW exception Move disk from used to free list
于是我的代码和他几乎一模一样
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DiskFactory : MonoBehaviour { public DiskData prefab; List<DiskData> used = new List<DiskData>(); List<DiskData> free = new List<DiskData>(); public DiskData getDisk(Ruler ruler) { DiskData a_disk; if (free.Count > 0) { a_disk = free[0]; free.RemoveAt(0); } else { a_disk = Instantiate(prefab); } a_disk.ruler = ruler; used.Add(a_disk); return a_disk; } public void freeDisk(DiskData disk) { free.Add(disk); if (!used.Remove(disk)) { throw new System.Exception(); } } }
Ruler的作用就显现出来了:如果我们要生产一个飞碟,我们需要知道它的所有属性值,这些属性值由场景提供。如果没有Ruler类,那么getDisk的原型是这样的:
public DiskData getDisk(Color color, float size, Vector3 position, float speed, Vector3 direction)
冗长不美观是一个问题,但更严重的是,如果我想增加或者删除飞碟的属性,那么我就要修改getDisk的参数列表,那么会非常麻烦而且容易出错。
第五步:设置Ruler的目的还有另外一个就是方便对disk的属性进行修改
public class DiskData : MonoBehaviour { Ruler _ruler; public Ruler ruler { get { return _ruler; } set { _ruler = value; gameObject.GetComponent<Renderer>().material.color = value.color; gameObject.transform.localScale = new Vector3(value.size, 0.05f, value.size); gameObject.transform.position = value.position; } } }
联系到前面DiskFactory的代码:
a_disk.ruler = ruler;
这样就能轻松修改属性。
第六步:运动轨迹
这里的课程还没有教到刚体,因此用坐标变换来实现飞碟的运动。但其实利用刚体感觉更麻烦,而且unity那个施加力的动作至今我都觉得很迷。
我们使用一个方向向量来代表飞碟的方向。这个方向向量是一个单位向量。那么根据相似性,我们可以知道飞碟的初始速度的三个分量分别是初始速度的值乘上方向向量的对应分量的值,例如:
如果方向向量为(x,y,z),速度为a,那么x方向速度为a*x,以此类推。
然后联系到update的机制,update是不断更新的,所以我们可以用现在飞碟的状态,来算出下一时刻飞碟的状态。
在DiskData中增加三个变量:
public float vx, vy, vz;
ruler的set修改为:
public Ruler ruler { get { return _ruler; } set { _ruler = value; gameObject.GetComponent<Renderer>().material.color = value.color; gameObject.transform.localScale = new Vector3(value.size, 0.05f, value.size); gameObject.transform.position = value.position; vx = value.speed * value.direction.x; vy = value.speed * value.direction.y; vz = value.speed * value.direction.z; } }
然后在场景内增加一个runDisk的函数:
void runDisk(DiskData disk) { float xt1 = disk.gameObject.transform.position.x + disk.vx * Time.deltaTime; float zt1 = disk.gameObject.transform.position.z + disk.vz * Time.deltaTime; disk.vy -= gravity * Time.deltaTime; float yt1 = disk.gameObject.transform.position.y + disk.vy * Time.deltaTime; disk.gameObject.transform.position = new Vector3(xt1, yt1, zt1); }
上面代码都是简单的运动学公式。
第七步:
程序的所有代码都在下面的附件全部列出来了。现在将sceneController和DiskFactory挂载在一个空物体上,然后将刚才产生的预制,拖进DiskFactory的inspector标签里的prefab变量。就可以运行了。
你可能会发现里面还有很多代码我没说明,其实这些代码并不是关键,要么是它看起来就很容易理解(例如那个单例模式),要么就是有其它方法代替,例如那个ScoreController,其实完全可以不用。
后记:
这个打飞碟游戏其实还不完善。
- 分数直接print在console里
- round升级没有任何效果
附:所有代码
SSDirector.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SSDirector : Object { private static SSDirector _instance; public SceneController currentSceneController { get; set; } public bool running { get; set; } public static SSDirector getInstance() { if (_instance == null) { _instance = new SSDirector(); } return _instance; } public int getFPS() { return Application.targetFrameRate; } public void setFPS(int fps) { Application.targetFrameRate = fps; } public void NextScene() { } }
Singleton.cs:(这是单例模式)
using System.Collections; using System.Collections.Generic; using UnityEngine; 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 thre is none."); } } return instance; } } }
DiskData.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ruler
{
public Color color;
public float size;
public Vector3 position;
public float speed;
public Vector3 direction; // 单位向量
public Ruler(Color color, float size, Vector3 position, float speed, Vector3 direction)
{
this.color = color;
this.size = size;
this.position = position;
this.speed = speed;
this.direction = direction;
}
}
public class DiskData : MonoBehaviour {
Ruler _ruler;
public Ruler ruler { get { return _ruler; } set { _ruler = value; gameObject.GetComponent<Renderer>().material.color = value.color; gameObject.transform.localScale = new Vector3(value.size, 0.05f, value.size); gameObject.transform.position = value.position; vx = value.speed * value.direction.x; vy = value.speed * value.direction.y; vz = value.speed * value.direction.z; } }
public float vx, vy, vz;
// Use this for initialization
void Start () {
//gameObject.GetComponent<Renderer>().material.color = color;
//gameObject.transform.position = position;
//gameObject.transform.localScale = new Vector3(size, 0.05f, size);
}
// Update is called once per frame
void Update () {
}
}
DiskFactory.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DiskFactory : MonoBehaviour { public DiskData prefab; List<DiskData> used = new List<DiskData>(); List<DiskData> free = new List<DiskData>(); public DiskData getDisk(Ruler ruler) { DiskData a_disk; if (free.Count > 0) { a_disk = free[0]; free.RemoveAt(0); } else { a_disk = Instantiate(prefab); } a_disk.ruler = ruler; used.Add(a_disk); return a_disk; } public void freeDisk(DiskData disk) { free.Add(disk); if (!used.Remove(disk)) { throw new System.Exception(); } } // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }
SceneController.cs:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScoreController
{
public int score = 0;
public void record(DiskData disk)
{
score += Mathf.RoundToInt(disk.ruler.color.b + disk.ruler.color.g + disk.ruler.color.r);
score += Mathf.RoundToInt(disk.ruler.size);
score += Mathf.RoundToInt(disk.ruler.speed);
}
}
public class SceneController : MonoBehaviour {
List<DiskData> diskList;
DiskFactory diskFactory;
ScoreController scoreCtrl;
int round = 0;
void Awake()
{
SSDirector director = SSDirector.getInstance();
director.setFPS(60);
director.currentSceneController = this;
director.currentSceneController.LoadResources();
}
public void LoadResources()
{
diskList = new List<DiskData>();
diskFactory = Singleton<DiskFactory>.Instance;
scoreCtrl = new ScoreController();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
float interval = 0;
public int upperNum;
int leftNum;
public Camera cam;
Color[] colors = { Color.black, Color.blue, Color.cyan, Color.gray, Color.green, Color.magenta, Color.red, Color.white, Color.yellow };
void Update () {
interval += Time.deltaTime;
if (interval >= 2f)
{
int shootNum = UnityEngine.Random.Range(1, upperNum/3);
leftNum -= shootNum;
if (leftNum <= 0)
{
round++;
leftNum = upperNum;
}
for (int i = 0; i < shootNum; i++) {
Color color = colors[UnityEngine.Random.Range(0, 9)];
float size = UnityEngine.Random.Range(1f, 3f);
Vector3 position = new Vector3(UnityEngine.Random.Range(-10f, 10f), 0, 0);
float speed = UnityEngine.Random.Range(5f, 100f);
Vector3 direction = new Vector3(
UnityEngine.Random.Range(-1000f, 1000f),
UnityEngine.Random.Range(0, 1000f),
UnityEngine.Random.Range(0, 1000f)
);
direction.Normalize();
Ruler ruler = new Ruler(color, size, position, speed, direction);
diskList.Add(diskFactory.getDisk(ruler));
}
interval = 0;
}
for (int i = 0; i < diskList.Count; i++)
{
if (diskList[i].gameObject.transform.position.y < 0)
{
recycleDisk(diskList[i]);
}
}
for (int i = 0; i < diskList.Count; i++)
{
runDisk(diskList[i]);
}
if (Input.GetButtonDown("Fire1"))
{
Vector3 mousePosition = Input.mousePosition;
Ray ray = cam.ScreenPointToRay(mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
scoreCtrl.record(hit.transform.gameObject.GetComponent<DiskData>());
recycleDisk(hit.transform.gameObject.GetComponent<DiskData>());
print(scoreCtrl.score);
}
}
}
void recycleDisk(DiskData disk)
{
disk.gameObject.transform.position = new Vector3(0, 0, -100);
diskFactory.freeDisk(disk);
diskList.Remove(disk);
}
public float gravity = 9.8f;
void runDisk(DiskData disk) { float xt1 = disk.gameObject.transform.position.x + disk.vx * Time.deltaTime; float zt1 = disk.gameObject.transform.position.z + disk.vz * Time.deltaTime; disk.vy -= gravity * Time.deltaTime; float yt1 = disk.gameObject.transform.position.y + disk.vy * Time.deltaTime; disk.gameObject.transform.position = new Vector3(xt1, yt1, zt1); }
}
相关文章推荐
- Unity学习笔记5:多人游戏基础
- Unity学习笔记6:多人游戏进阶篇
- 【Unity 3D】学习笔记二十六:unity游戏脚本(六)
- 【Unity 3D】学习笔记三十二:游戏元素——游戏光源
- 【Unity 3D】学习笔记三十一:游戏元素——地形元素
- Unity学习系类笔记5:多人游戏基础
- 【Unity 3D】学习笔记四十五:游戏实例——击垮围墙
- 【Unity 3D】学习笔记二十四:unity游戏脚本(四)
- 【Unity 3D】学习笔记二十三:unity游戏脚本(三)
- 【Unity 3D】学习笔记三十四:游戏元素——常用编辑器组件
- Unity学习笔记-2D横版游戏中的背景移动
- Unity学习笔记3 简易2D横版RPG游戏制作(三)
- Unity学习笔记2 简易2D横版RPG游戏制作(二)
- 【Unity 3D】学习笔记二十五:unity游戏脚本(五)
- 【Unity 3D】学习笔记二十:基础2D游戏界面
- Unity学习笔记5:多人游戏基础
- Unity学习笔记1 简易2D横版RPG游戏制作(一)
- 【Unity 3D】学习笔记十八:游戏贴图与帧动画
- 【Unity 3D】学习笔记二十九:游戏实例——简单小地图制作
- 【Unity 3D】学习笔记三十五:游戏实例——摄像机切换镜头