空间与运动
1、简答并用程序验证
(1)游戏对象运动的本质是什么
游戏对象运动本质就是使用矩阵变换(平移、旋转、缩放)改变游戏对象的空间属性(位置、欧拉角、比例)。
(2)请用三种方法以上方法,实现物体的抛物线运动
第一种方法
利用transform改变position来实现抛物线运动,水平方向的移动速度是不变的,竖直方向是有一定的加速度变化的,两个方向的运动矢量相加即可实现抛物线运动。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class paowu1 : MonoBehaviour {
public float speed;
// Use this for initialization
void Start () {
speed = 0;
}
// Update is called once per frame
void Update () {
this.transform.position += Vector3.right * Time.deltaTime * 5;
this.transform.position += Vector3.down * Time.deltaTime * (speed/10);
speed++;
}
}
第二种方法
声明一个Vector3变量,通过改变该变量打到抛物的矢量变化,然后与对象的position相加。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class paowu2 : MonoBehaviour {
public float speed;
// Use this for initialization
void Start () {
speed = 0;
}
// Update is called once per frame
void Update () {
Vector3 vec = new Vector3( Time.deltaTime*5, -Time.deltaTime*(speed/10), 0);
speed++;
this.transform.position += vec;
}
}
第三种方法
利用transform的Translate函数,和上种方法类似,传入一个Vector3变量来改变position。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class paowu3 : MonoBehaviour {
public float speed;
// Use this for initialization
void Start () {
speed = 0;
}
// Update is called once per frame
void Update () {
Vector3 vec = new Vector3(Time.deltaTime * 5, -Time.deltaTime * (speed / 10), 0);
speed++;
transform.Translate(vec);
}
}
(3)写一个程序,实现一个完整的太阳系, 其他星球围绕太阳的转速必须不一样,且不在一个法平面上。
首先上网下载太阳系贴图
太阳系贴图
将贴图图片拖入Assets,再拖放到球体对象上,就能成功贴图。以此方法按太阳系球体的大小、位置创建各个星球(题目中要求各星球不在同一法平面,可以适当调整位置)。
初始位置如下:
根据初始位置算出各个星球绕太阳旋转的轴(行星球心与太阳球心连线的垂直轴),利用Rotate()进行自转,利用RotateAround()进行公转。然后将各Transform变量与Unity中的对象联系即可。其中月亮属于地球的child。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class taiyangxi : MonoBehaviour
{
public Transform sun;
public Transform shuixing;
public Transform jinxing;
public Transform earth;
public Transform huoxing;
public Transform muxing;
public Transform tuxing;
public Transform tianwangxing;
public Transform haiwangxing;
public Transform moon;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
sun.Rotate(Vector3.up * 30 * Time.deltaTime);
shuixing.Rotate(Vector3.up * 40 * Time.deltaTime);
shuixing.RotateAround(sun.position, new Vector3(1, -8, 0), 30 * Time.deltaTime);
jinxing.Rotate(Vector3.up * 50 * Time.deltaTime);
jinxing.RotateAround(sun.position, new Vector3(10, -23, 0), 100 * Time.deltaTime);
earth.Rotate(Vector3.up * 60 * Time.deltaTime);
earth.RotateAround(sun.position, new Vector3(0, 1, 0), 50 * Time.deltaTime);
huoxing.Rotate(Vector3.up * 40 * Time.deltaTime);
huoxing.RotateAround(sun.position, new Vector3(1, -2, 0), 70 * Time.deltaTime);
muxing.Rotate(Vector3.up * 20 * Time.deltaTime);
muxing.RotateAround(sun.position, new Vector3(2, 23, 0), 30 * Time.deltaTime);
tuxing.Rotate(Vector3.up * 10 * Time.deltaTime);
tuxing.RotateAround(sun.position, new Vector3(2, -11, 0), 40 * Time.deltaTime);
tianwangxing.Rotate(Vector3.up * 30 * Time.deltaTime);
tianwangxing.RotateAround(sun.position, new Vector3(3, -62, 0), 20 * Time.deltaTime);
haiwangxing.Rotate(Vector3.up * 60 * Time.deltaTime);
haiwangxing.RotateAround(sun.position, new Vector3(3, 34, 0), 100 * Time.deltaTime);
moon.Rotate(Vector3.up * 60 * Time.deltaTime);
moon.RotateAround(earth.position, Vector3.up, 500 * Time.deltaTime);
}
}
旋转过程效果图如下:
2、编程实践
(1)列出游戏中提及的事务(Objects)
游戏中的对象有:牧师、魔鬼、小船、开始岸、结束岸
(2)用表格列出玩家动作表(规则表)
动作 | 条件 |
---|---|
开船 | 船上有对象 |
牧师上船 | 船在岸边,岸上有牧师,船上少于两个对象 |
魔鬼上船 | 船在岸边,岸上有魔鬼,船上少于两个对象 |
船上左边对象下船 | 船在岸边,船上左边有对象 |
船上右边对象下船 | 船在岸边,船上右边有对象 |
(3)将游戏中对象做成预制
将游戏中需要的对象进行预制,分别是正方体的牧师、球体的魔鬼、长方体的小船和长方体的岸边。代码中需要使用Resources类,因此需要在Assets文件夹下创建一个Resources文件夹,并将预制的对象放入其中。
相关参考博客:
Unity3D的Resource类使用
(4)MVC结构程序
分别创建三个脚本Model、View、Controller。按要求游戏只能有主摄像机和一个empty对象,其中Model和Controller脚本挂在主摄像机下,View挂在empty下。
(5)游戏具体设计
游戏开始界面:
游戏成功界面:
游戏失败界面:
Model
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
该脚本中定义了游戏对象的位置和完善游戏动作的函数,同时负责加载预制的游戏对象。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using mygame;
public class Model : MonoBehaviour {
Stack<GameObject> start_priests = new Stack<GameObject>();
Stack<GameObject> end_priests = new Stack<GameObject>();
Stack<GameObject> start_devils = new Stack<GameObject>();
Stack<GameObject> end_devils = new Stack<GameObject>();
GameObject[] boat = new GameObject[2];
GameObject boat_obj;
public float speed = 50;
SSDirector one;
//对象的位置
Vector3 boatStartPos = new Vector3(-6, 0, 0);
Vector3 boatEndPos = new Vector3(6, 0, 0);
Vector3 sideStartPos = new Vector3(-10, 0, 0);
Vector3 sideEndPos = new Vector3(10, 0, 0);
float gap = 1.1f;
Vector3 priestsStartPos = new Vector3(-11, 1, 0);
Vector3 priestsEndPos = new Vector3(13, 1, 0);
Vector3 devilsStartPos = new Vector3(-7, 1, 0);
Vector3 devilsEndPos = new Vector3(10, 1, 0);
// Use this for initialization
void Start () {
one = SSDirector.GetInstance();
one.setModel(this);
loadSrc();
}
// Update is called once per frame
void Update () {
setposition(start_priests, priestsStartPos);
setposition(end_priests, priestsEndPos);
setposition(start_devils, devilsStartPos);
setposition(end_devils, devilsEndPos);
if(one.state == State.SE_move)
{
boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatEndPos, Time.deltaTime * speed);
if (boat_obj.transform.position == boatEndPos)
{
one.state = State.End;
}
}
else if(one.state == State.ES_move)
{
boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatStartPos, Time.deltaTime * speed);
if (boat_obj.transform.position == boatStartPos)
{
one.state = State.Start;
}
}
else
{
check();
}
}
//加载游戏对象
void loadSrc()
{
//sides
Instantiate(Resources.Load("sides"), sideStartPos, Quaternion.identity);
Instantiate(Resources.Load("sides"), sideEndPos, Quaternion.identity);
//boat
boat_obj = Instantiate(Resources.Load("boat"), boatStartPos, Quaternion.identity) as GameObject;
//prisets and devils
for(int i = 0; i < 3; i++)
{
start_priests.Push(Instantiate(Resources.Load("Priests")) as GameObject);
start_devils.Push(Instantiate(Resources.Load("Devils")) as GameObject);
}
}
void setposition(Stack<GameObject> aaa, Vector3 pos)
{
GameObject[] temp = aaa.ToArray();
for(int i = 0; i < aaa.Count; i++)
{
temp[i].transform.position = pos + new Vector3(-gap * i, 0, 0);
}
}
//上船
void getOnTheBoat(GameObject obj)
{
obj.transform.parent = boat_obj.transform;
if(boatNum() != 0)
{
if (boat[0] == null)
{
boat[0] = obj;
obj.transform.localPosition = new Vector3(-0.3f, 2.5f, 0);
}
else
{
boat[1] = obj;
obj.transform.localPosition = new Vector3(0.3f, 2.5f, 0);
}
}
}
//判断船上是否有空位
int boatNum()
{
int num = 0;
for(int i = 0; i < 2; i++)
{
if (boat[i] == null)
{
num++;
}
}
return num;
}
//船移动
public void moveBoat()
{
if(boatNum() != 2)
{
if(one.state == State.Start)
{
one.state = State.SE_move;
}
else if(one.state == State.End)
{
one.state = State.ES_move;
}
}
}
//下船
public void getOffTheBoat(int side)
{
if (boat[side] != null)
{
boat[side].transform.parent = null;
if(one.state == State.Start)
{
if (boat[side].tag == "Priests")
{
start_priests.Push(boat[side]);
}
else
{
start_devils.Push(boat[side]);
}
}
else if(one.state == State.End)
{
if (boat[side].tag == "Priests")
{
end_priests.Push(boat[side]);
}
else
{
end_devils.Push(boat[side]);
}
}
boat[side] = null;
}
}
void check()
{
if(end_devils.Count == 3 && end_priests.Count == 3)
{
one.state = State.Win;
return;
}
int bp = 0, bd = 0;
for(int i = 0; i < 2; i++)
{
if (boat[i] != null && boat[i].tag == "Priests")
{
bp++;
}
else if (boat[i] != null && boat[i].tag == "Devils")
{
bd++;
}
}
int sp = 0, sd = 0, ep = 0, ed = 0;
if(one.state == State.Start)
{
sp = start_priests.Count + bp;
ep = end_priests.Count;
sd = start_devils.Count + bd;
ed = end_devils.Count;
}
else if(one.state == State.End)
{
sp = start_priests.Count;
ep = end_priests.Count + bp;
sd = start_devils.Count;
ed = end_devils.Count + bd;
}
if((sp != 0 && sp < sd) || (ep != 0 && ep < ed))
{
one.state = State.Lose;
}
}
//游戏对象从岸上到船上的变化
public void priS()
{
if(start_priests.Count != 0 && boatNum() != 0 && one.state == State.Start)
{
getOnTheBoat(start_priests.Pop());
}
}
public void priE()
{
if(end_priests.Count != 0 && boatNum() != 0 && one.state == State.End)
{
getOnTheBoat(end_priests.Pop());
}
}
public void delS()
{
if(start_devils.Count != 0 && boatNum() != 0 && one.state == State.Start)
{
getOnTheBoat(start_devils.Pop());
}
}
public void delE()
{
if(end_devils.Count != 0 && boatNum() != 0 && one.state == State.End)
{
getOnTheBoat(end_devils.Pop());
}
}
//重置游戏
public void Reset()
{
boat_obj.transform.position = boatStartPos;
one.state = State.Start;
int num1 = end_devils.Count, num2 = end_priests.Count;
for(int i = 0; i < num1; i++)
{
Debug.Log(i);
start_devils.Push(end_devils.Pop());
}
for (int i = 0; i < num2; i++)
{
start_priests.Push(end_priests.Pop());
}
getOffTheBoat(0);
getOffTheBoat(1);
}
}
View
View(视图)是应用程序中处理数据显示的部分。
该脚本中设置了游戏的UI即游戏需要的Button。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using mygame;
public class View : MonoBehaviour
{
SSDirector one;
UserAction action;
// Use this for initialization
void Start()
{
one = SSDirector.GetInstance();
action = SSDirector.GetInstance() as UserAction;
}
private void OnGUI()
{
GUI.skin.label.fontSize = 30;
if (one.state == State.Win)
{
if (GUI.Button(new Rect(350, 150, 300, 100), "WIN\n(click here to reset)"))
{
action.reset();
}
}
if (one.state == State.Lose)
{
if (GUI.Button(new Rect(350, 150, 300, 100), "LOSE\n(click here to reset)"))
{
action.reset();
}
}
if (GUI.Button(new Rect(450, 80, 100, 50), "GO"))
{
action.moveBoat();
}
if (GUI.Button(new Rect(350, 300, 75, 50), "LeftOFF"))
{
action.offBoatL();
}
if (GUI.Button(new Rect(550, 300, 75, 50), "RightOFF"))
{
action.offBoatR();
}
if (GUI.Button(new Rect(50, 130, 75, 50), "PriestsON"))
{
action.priestSOnSide();
}
if (GUI.Button(new Rect(200, 130, 75, 50), "DevilsON"))
{
action.devilSOnSide();
}
if (GUI.Button(new Rect(750, 130, 75, 50), "DevilsON"))
{
action.devilEOnSide();
}
if (GUI.Button(new Rect(900, 130, 75, 50), "PriestsON"))
{
action.priestEOnSide();
}
}
}
Controller
Controller(控制器)是应用程序中处理用户交互的部分。
该脚本中定义了一个命名空间mygame,用于调用Model中的函数。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using mygame;
namespace mygame
{
public enum State { Start, SE_move, ES_move, End, Win, Lose };
public interface UserAction {
void priestSOnSide();
void priestEOnSide();
void devilSOnSide();
void devilEOnSide();
void moveBoat();
void offBoatL();
void offBoatR();
void reset();
}
public class SSDirector : System.Object, UserAction
{
private static SSDirector _instance;
public Controller currentScenceController;
public State state = State.Start;
private Model game_obj;
public static SSDirector GetInstance()
{
if(_instance == null)
{
_instance = new SSDirector();
}
return _instance;
}
public Model getModel()
{
return game_obj;
}
internal void setModel(Model someone)
{
if(game_obj == null)
{
game_obj = someone;
}
}
public void priestSOnSide()
{
game_obj.priS();
}
public void priestEOnSide()
{
game_obj.priE();
}
public void devilSOnSide()
{
game_obj.delS();
}
public void devilEOnSide()
{
game_obj.delE();
}
public void moveBoat()
{
game_obj.moveBoat();
}
public void offBoatL()
{
game_obj.getOffTheBoat(0);
}
public void offBoatR()
{
game_obj.getOffTheBoat(1);
}
public void reset()
{
game_obj.Reset();
}
}
}
public class Controller : MonoBehaviour {
// Use this for initialization
void Start()
{
SSDirector one = SSDirector.GetInstance();
}
// Update is called once per frame
void Update()
{
}
}
游戏视频链接如下:
Priests&Devils