3D遊戲-作業三-空間與運動
一、簡答並用程式驗證
1. 遊戲物件運動的本質是什麼?
遊戲物件運動的本質就是它的空間屬性的變化,包括空間位置,旋轉角度,放縮大小等等
2. 請用三種方法以上方法,實現物體的拋物線運動。(如,修改Transform屬性,使用向量Vector3的方法…)
方法一 直接修改Transform屬性
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class method1 : MonoBehaviour
{
private float vX = 5;
private float vY = 0;
private float g = 10;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
vY += g * Time.deltaTime;
transform.position += Vector3.right * vX * Time.deltaTime;
transform.position += Vector3.down * vY * Time.deltaTime;
}
}
方法二 通過建立Vector3來修改Transform屬性
此時只需要修改Update函式部分就可以了
void Update()
{
vY += g * Time.deltaTime;
Vector3 temp = new Vector3(vX * Time.deltaTime, - vY * Time.deltaTime, 0);
transform.position += temp;
}
方法三 transform的Translate方法
同樣修改Update函式,如下
void Update()
{
vY += g * Time.deltaTime;
Vector3 temp = new Vector3(vX * Time.deltaTime, - vY * Time.deltaTime, 0);
transform.Translate(temp);
}
方法四 通過修改物體的重力屬性
這個方法是我搜尋得知物件的重力屬性,然後修改這個屬性實現的
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class method4 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
gameObject.AddComponent<Rigidbody>();
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(2, 0, 0);
}
// Update is called once per frame
void Update() { }
}
3. 寫一個程式,實現一個完整的太陽系, 其他星球圍繞太陽的轉速必須不一樣,且不在一個法平面上。
首先建立太陽系
建立好各個行星,並調整它們的位置和大小,貼上相應的圖片
編寫程式碼,實現自轉和公轉
在這裡,自轉通過函式Rotate來實現,公轉通過函式RotateAround來實現,程式碼如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class solarSystem : MonoBehaviour
{
public Transform sun;
public Transform mercury;
public Transform venus;
public Transform earth;
public Transform mars;
public Transform jupiter;
public Transform saturn;
public Transform uranus;
public Transform neptune;
// Start is called before the first frame update
void Start(){ }
// Update is called once per frame
void Update()
{
//RotateAround 實現公轉
//Rotate實現自轉
sun.Rotate(Vector3.up * Time.deltaTime * 5);
mercury.RotateAround(this.transform.position, new Vector3(3, 15, 0), 47 * Time.deltaTime);
mercury.Rotate(Vector3.up * Time.deltaTime * 300);
venus.RotateAround(this.transform.position, new Vector3(2,10, 0), 35 * Time.deltaTime);
venus.Rotate(Vector3.up * Time.deltaTime * 280);
earth.RotateAround(this.transform.position, new Vector3(1, 10, 0), 30 * Time.deltaTime);
earth.Rotate(Vector3.up * Time.deltaTime * 250);
mars.RotateAround(this.transform.position, new Vector3(2, 15, 0), 24 * Time.deltaTime);
mars.Rotate(Vector3.up * Time.deltaTime * 220);
jupiter.RotateAround(this.transform.position, new Vector3(2, 10, 0), 13 * Time.deltaTime);
jupiter.Rotate(Vector3.up * Time.deltaTime * 180);
saturn.RotateAround(this.transform.position, new Vector3(1, 10, 0), 9 * Time.deltaTime);
saturn.Rotate(Vector3.up * Time.deltaTime * 160);
uranus.RotateAround(this.transform.position, new Vector3(2, 10, 0), 6 * Time.deltaTime);
uranus.Rotate(Vector3.up * Time.deltaTime * 150);
neptune.RotateAround(this.transform.position, new Vector3(3, 15, 0), 5 * Time.deltaTime);
neptune.Rotate(Vector3.up * Time.deltaTime * 140);
}
}
此時還有一個問題,就是怎麼把地月系統加到現在的這個太陽系裡,因為上面這個系統中地球是在自轉的,直接把月球繞它轉的話,就必須要考慮到自轉對月球公轉的影響,所以在這裡我們採用另一種八法來解決這個問題。
那就是建立一個新物件,他的位置大小都與地球相同,同樣繞太陽公轉,但是不自轉,這樣的話,就可以讓月球來繞它自轉,這樣就解決了這個問題。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class solarSystem : MonoBehaviour
{
public Transform moon;
public Transform earthclone;
// Start is called before the first frame update
void Start(){ }
// Update is called once per frame
void Update()
{
//實現地月系統
earthclone.RotateAround(this.transform.position, new Vector3(1, 10, 0), 30 * Time.deltaTime);
moon.transform.RotateAround (earthclone.transform.position, new Vector3(0, 12, 0), 500 * Time.deltaTime);
}
}
這樣就完成了太陽系模擬。
顯示軌跡
雖然已經完成了太陽系模擬的過程,但是在執行的時候可以發現,我們看著這樣雜亂無章的效果很頭疼,完全看不到行星運動軌跡,這壓根的話就不能體現這個太陽系的行星軌道之間的區別等等,所以通過搜尋,我找到了一個解決辦法。
那就是通過Trail Render來顯示運動軌跡。
這裡是參考部落格
以上就是最後實現的效果。
二、 程式設計實踐-牧師與魔鬼
Priests and Devils
Priests and Devils is a puzzle game in which you will help the Priests and Devils to cross the river within the time limit. There are 3 priests and 3 devils at one side of the river. They all want to get to the other side of this river, but there is only one boat and this boat can only carry two persons each time. And there must be one person steering the boat from one side to the other side. In the flash game, you can click on them to move them and click the go button to move the boat to the other direction. If the priests are out numbered by the devils on either side of the river, they get killed and the game is over. You can try it in many > ways. Keep all priests alive! Good luck!
程式需要滿足的要求
play the game ( http://www.flash-game.net/game/2535/priests-and-devils.html )
列出遊戲中提及的事物(Objects)
用表格列出玩家動作表(規則表),注意,動作越少越好
請將遊戲中物件做成預製
在場景控制器 LoadResources 方法中載入並初始化 長方形、正方形、球 及其色彩代表遊戲中的物件。
使用 C# 集合型別 有效組織物件
整個遊戲僅 主攝像機 和 一個 Empty 物件, 其他物件必須程式碼動態生成!!! 。 整個遊戲不許出現 Find 遊戲物件, SendMessage 這類突破程式結構的 通訊耦合 語句。 違背本條準則,不給分
請使用課件架構圖程式設計,不接受非 MVC 結構程式
注意細節,例如:船未靠岸,牧師與魔鬼上下船運動中,均不能接受使用者事件!
遊戲中提及的事物
牧師(3個)、魔鬼(3個)、船、河流、河岸(兩側)
玩家動作表(規則表)
玩家動作 | 條件 | 執行結果 |
---|---|---|
點選岸上的牧師或魔鬼 | 當前船在岸邊,且船未滿 | 點選物件上船 |
點選船上的牧師或魔鬼 | 當前船在岸邊 | 點選物件上岸 |
點選船 | 當前船在岸邊,且船上至少有一個牧師或魔鬼 | 船移動到對岸 |
遊戲物件預製
預製結果如下
根據MVC架構程式設計
MVC架構
實現過程
首先實現SSDirector
它的功能是控制場景切換,在這裡它繼承於System.Object,保持導演類一直存在,不被Unity記憶體管理而管理
實現程式碼如下:
public class SSDirector : System.Object
{
private static SSDirector _instance;
public ISceneController CurrentScenceController { get; set; }
public static SSDirector GetInstance()
{
if (_instance == null)
{
_instance = new SSDirector();
}
return _instance;
}
}
接下來就是建立介面
介面的實現,方便了其他模組呼叫此名稱空間。分別是場景控制器的介面,利用這個介面,得知當前場景是由哪個控制,然後向場景控制器傳達要求,以及使用者動作的介面,使用者通過鍵盤、滑鼠等對遊戲發出指令,這個指令會觸發遊戲中的一些行為,這一部分由IUserAction來宣告。
namespace interfacecon{
public interface ISceneController
{
void LoadResources();
}
public interface IUserAction
{
void MoveBoat();
void Restart();
void MoveRole(RoleModel role);
int Check();
}
}
建立使用者介面
在這裡,包括左上角的遊戲規則介紹按鈕,以及在遊戲結束之後的彈出按鈕和標籤等等
public class UserGUI : MonoBehaviour {
private IUserAction action;
public int sign = 0;
bool isShow = false;
void Start()
{
action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
}
void OnGUI()
{
GUIStyle text_style;
GUIStyle button_style;
text_style = new GUIStyle()
{
fontSize = 30
};
button_style = new GUIStyle("button")
{
fontSize = 15
};
if (GUI.Button(new Rect(10, 10, 60, 30), "Rule", button_style))
{
if (isShow)
isShow = false;
else
isShow = true;
}
if(isShow)
{
GUI.Label(new Rect(Screen.width / 2 - 85, 10, 200, 50), "讓全部牧師和惡魔都渡河");
GUI.Label(new Rect(Screen.width / 2 - 120, 30, 250, 50), "每一邊惡魔數量都不能多於牧師數量");
GUI.Label(new Rect(Screen.width / 2 - 85, 50, 250, 50), "點選牧師、惡魔、船移動");
}
if (sign == 1)
{
GUI.Label(new Rect(Screen.width / 2-90, Screen.height / 2-120, 100, 50), "Gameover!", text_style);
if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 100, 50), "Restart", button_style))
{
action.Restart();
sign = 0;
}
}
else if (sign == 2)
{
GUI.Label(new Rect(Screen.width / 2 - 80, Screen.height / 2 - 120, 100, 50), "You Win!", text_style);
if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 100, 50), "Restart", button_style))
{
action.Restart();
sign = 0;
}
}
}
}
實現控制器
它對場景中的具體物件進行操作,是遊戲物件的行為改變的核心。
public class Controller : MonoBehaviour, ISceneController, IUserAction
{
public LandModel start_land;
public LandModel end_land;
public BoatModel boat;
private RoleModel[] roles;
UserGUI user_gui;
void Start ()
{
SSDirector director = SSDirector.GetInstance();
director.CurrentScenceController = this;
user_gui = gameObject.AddComponent<UserGUI>() as UserGUI;
LoadResources();
}
public void LoadResources()
{
GameObject water = Instantiate(Resources.Load("Prefabs/Water", typeof(GameObject)), new Vector3(0,-10,-2), Quaternion.identity) as GameObject;
water.name = "water";
start_land = new LandModel("start");
end_land = new LandModel("end");
boat = new BoatModel();
roles = new RoleModel[6];
for (int i = 0; i < 3; i++)
{
RoleModel role = new RoleModel("priest");
role.SetName("priest" + i);
role.SetPosition(start_land.GetEmptyPosition());
role.GoLand(start_land);
start_land.AddRole(role);
roles[i] = role;
}
for (int i = 3; i < 6; i++)
{
RoleModel role = new RoleModel("devil");
role.SetName("devil" + i);
role.SetPosition(start_land.GetEmptyPosition());
role.GoLand(start_land);
start_land.AddRole(role);
roles[i] = role;
}
}
public void MoveBoat()
{
if (boat.IsEmpty() || user_gui.sign != 0) return;
boat.BoatMove();
user_gui.sign = Check();
}
public void MoveRole(RoleModel role)
{
if (user_gui.sign != 0) return;
if (role.IsOnBoat())
{
LandModel land;
if (boat.GetBoatSign() == -1)
land = end_land;
else
land = start_land;
boat.DeleteRoleByName(role.GetName());
role.Move(land.GetEmptyPosition());
role.GoLand(land);
land.AddRole(role);
}
else
{
LandModel land = role.GetLandModel();
if (boat.GetEmptyNumber() == -1 || land.GetLandSign() != boat.GetBoatSign()) return;
land.DeleteRoleByName(role.GetName());
role.Move(boat.GetEmptyPosition());
role.GoBoat(boat);
boat.AddRole(role);
}
user_gui.sign = Check();
}
public void Restart()
{
SceneManager.LoadScene(0);
}
public int Check()
{
int start_priest = (start_land.GetRoleNum())[0];
int start_devil = (start_land.GetRoleNum())[1];
int end_priest = (end_land.GetRoleNum())[0];
int end_devil = (end_land.GetRoleNum())[1];
if (end_priest + end_devil == 6)
return 2;
int[] boat_role_num = boat.GetRoleNumber();
if (boat.GetBoatSign() == 1)
{
start_priest += boat_role_num[0];
start_devil += boat_role_num[1];
}
else
{
end_priest += boat_role_num[0];
end_devil += boat_role_num[1];
}
if (start_priest > 0 && start_priest < start_devil)
{
return 1;
}
if (end_priest > 0 && end_priest < end_devil)
{
return 1;
}
return 0;
}
}
接下來就是ClickController
它的功能是檢測船和牧師與魔鬼是否被點選,然後進行操作。
public class Click : MonoBehaviour
{
IUserAction action;
RoleModel role = null;
BoatModel boat = null;
public void SetRole(RoleModel role)
{
this.role = role;
}
public void SetBoat(BoatModel boat)
{
this.boat = boat;
}
void Start()
{
action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
}
void OnMouseDown()
{
if (boat == null && role == null) return;
if (boat != null)
action.MoveBoat();
else if(role != null)
action.MoveRole(role);
}
}
最後是對具體遊戲物件的操作
首先是對船的操作,包括船的運動,以及牧師與魔鬼上下船的繫結
public class BoatModel
{
GameObject boat;
Vector3[] start_empty_pos;
Vector3[] end_empty_pos;
Move move;
Click click;
int boat_sign = 1;
RoleModel[] roles = new RoleModel[2];
public BoatModel()
{
boat = Object.Instantiate(Resources.Load("Prefabs/Boat", typeof(GameObject)), new Vector3(25, -2.5F, 0), Quaternion.identity) as GameObject;
boat.name = "boat";
move = boat.AddComponent(typeof(Move)) as Move;
click = boat.AddComponent(typeof(Click)) as Click;
click.SetBoat(this);
start_empty_pos = new Vector3[] { new Vector3(18, 4, 0), new Vector3(32,4 , 0) };
end_empty_pos = new Vector3[] { new Vector3(-32, 4, 0), new Vector3(-18,3 , 0) };
}
public bool IsEmpty()
{
for (int i = 0; i < roles.Length; i++)
{
if (roles[i] != null)
return false;
}
return true;
}
public void BoatMove()
{
if (boat_sign == -1)
{
move.MovePosition(new Vector3(25, -2.5F, 0));
boat_sign = 1;
}
else
{
move.MovePosition(new Vector3(-25, -2.5F, 0));
boat_sign = -1;
}
}
public int GetBoatSign(){ return boat_sign;}
public RoleModel DeleteRoleByName(string role_name)
{
for (int i = 0; i < roles.Length; i++)
{
if (roles[i] != null && roles[i].GetName() == role_name)
{
RoleModel role = roles[i];
roles[i] = null;
return role;
}
}
return null;
}
public int GetEmptyNumber()
{
for (int i = 0; i < roles.Length; i++)
{
if (roles[i] == null)
{
return i;
}
}
return -1;
}
public Vector3 GetEmptyPosition()
{
Vector3 pos;
if (boat_sign == -1)
pos = end_empty_pos[GetEmptyNumber()];
else
pos = start_empty_pos[GetEmptyNumber()];
return pos;
}
public void AddRole(RoleModel role)
{
roles[GetEmptyNumber()] = role;
}
public GameObject GetBoat(){ return boat; }
public int[] GetRoleNumber()
{
int[] count = { 0, 0 };
for (int i = 0; i < roles.Length; i++)
{
if (roles[i] == null)
continue;
if (roles[i].GetSign() == 0)
count[0]++;
else
count[1]++;
}
return count;
}
}
然後是對牧師與魔鬼(即role)的操作
包括上船、上岸
public class RoleModel
{
GameObject role;
int role_sign;
Click click;
bool on_boat;
Move move;
LandModel land_model = (SSDirector.GetInstance().CurrentScenceController as Controller).start_land;
public RoleModel(string role_name)
{
if (role_name == "priest")
{
role = Object.Instantiate(Resources.Load("Prefabs/Priests", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -70, 0)) as GameObject;
role_sign = 0;
}
else
{
role = Object.Instantiate(Resources.Load("Prefabs/Devils", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -70, 0)) as GameObject;
role_sign = 1;
}
move = role.AddComponent(typeof(Move)) as Move;
click = role.AddComponent(typeof(Click)) as Click;
click.SetRole(this);
}
public int GetSign() { return role_sign;}
public LandModel GetLandModel(){return land_model;}
public string GetName() { return role.name; }
public bool IsOnBoat() { return on_boat; }
public void SetName(string name) { role.name = name; }
public void SetPosition(Vector3 pos) { role.transform.position = pos; }
public void Move(Vector3 vec)
{
move.MovePosition(vec);
}
public void GoLand(LandModel land)
{
role.transform.parent = null;
land_model = land;
on_boat = false;
}
public void GoBoat(BoatModel boat)
{
role.transform.parent = boat.GetBoat().transform;
land_model = null;
on_boat = true;
}
}
對陸地的操作
用於控制角色上下岸,船的離開和停靠
public class LandModel
{
GameObject land;
Vector3[] positions;
int land_sign;
RoleModel[] roles = new RoleModel[6];
public LandModel(string land_mark)
{
positions = new Vector3[] {new Vector3(46F,14.73F,-4), new Vector3(55,14.73F,-4), new Vector3(64F,14.73F,-4),
new Vector3(73F,14.73F,-4), new Vector3(82F,14.73F,-4), new Vector3(91F,14.73F,-4)};
if (land_mark == "start")
{
land = Object.Instantiate(Resources.Load("Prefabs/Land", typeof(GameObject)), new Vector3(70, 1, 0), Quaternion.identity) as GameObject;
land_sign = 1;
}
else if(land_mark == "end")
{
land = Object.Instantiate(Resources.Load("Prefabs/Land", typeof(GameObject)), new Vector3(-70, 1, 0), Quaternion.identity) as GameObject;
land_sign = -1;
}
}
public int GetEmptyNumber()
{
for (int i = 0; i < roles.Length; i++)
{
if (roles[i] == null)
return i;
}
return -1;
}
public int GetLandSign() { return land_sign; }
public Vector3 GetEmptyPosition()
{
Vector3 pos = positions[GetEmptyNumber()];
pos.x = land_sign * pos.x;
return pos;
}
public void AddRole(RoleModel role)
{
roles[GetEmptyNumber()] = role;
}
public RoleModel DeleteRoleByName(string role_name)
{
for (int i = 0; i < roles.Length; i++)
{
if (roles[i] != null && roles[i].GetName() == role_name)
{
RoleModel role = roles[i];
roles[i] = null;
return role;
}
}
return null;
}
public int[] GetRoleNum()
{
int[] count = { 0, 0 };
for (int i = 0; i < roles.Length; i++)
{
if (roles[i] != null)
{
if (roles[i].GetSign() == 0)
count[0]++;
else
count[1]++;
}
}
return count;
}
}
最後,只需要將Controller掛載到新建的空物件上,就可以執行遊戲了,示例一局的結果如下:
思考題
使用向量與變換,實現並擴充套件 Tranform 提供的方法,如 Rotate、RotateAround 等
法一 直接通過公式計算
根據旋轉後點的位置的變換公式,可以直接計算得到旋轉後的位置,然後修改物體的空間屬性就可以實現旋轉
void Update () {
float delta = Mathf.Atan (this.transform.position.y / this.transform.position.x);
float temp_x = this.transform.position.x;
float temp_y = this.transform.position.y;
this.transform.position.x = Mathf.Cos (delta) * temp_x - Mathf.Sin (delta) * temp_y;
this.transform.position.y = Mathf.Sin (delta) * temp_x + Mathf.Cos (delta) * temp_y;
}
法二 通過vector3來實現
int angle;
void Update () {
angle -= Mathf.Atan (this.transform.position.y / this.transform.position.x);
transform.Translate(new Vector3(Time.deltaTime * 10*Mathf.Cos(angle), Time.deltaTime * 10*Mathf.Sin(angle), 0));
}
相關文章
- 3d遊戲第三次作業3D遊戲
- 疊紙遊戲公佈新作《戀與深空》PV,一款3D戀愛動作手遊遊戲3D
- 從搭建到優化,《永劫無間》如何做遊戲動作與運動系統優化遊戲
- 3D遊戲:五、與遊戲世界互動3D遊戲
- 與遊戲世界互動-作業與練習(5)遊戲
- 3D遊戲程式設計作業93D遊戲程式設計
- 遊戲音樂的認識與製作三遊戲
- 《戀與深空》遊戲拆解遊戲
- 遊戲大地圖開發指南:遊戲外部空間設計遊戲地圖
- 製作3D小汽車遊戲(下)3D遊戲
- 製作3D小汽車遊戲(上)3D遊戲
- 均衡,存乎萬物之間——動作遊戲簡史遊戲
- 【CV】三維空間的旋轉問題(Rotation in 3D space)3D
- 奇幻妖怪世界能給生活模擬遊戲帶來多大的創作空間?遊戲
- 3D遊戲角色模型建模| ZBrush製作3D獸人3D遊戲模型ZBrush
- 動作與射擊漫談:格鬥遊戲中的動作設計遊戲
- 遊戲中的時空與探索遊戲
- 《異界鎖鏈》:動作遊戲的,不那麼動作遊戲的遊戲
- 建築&遊戲 | 對經典遊戲地圖的空間淺析遊戲地圖
- 3D遊戲程式設計作業第九章 UI系統3D遊戲程式設計UI
- 一個連續動作空間的SAC的例子
- 運用知名第三方IP製作遊戲面臨的三大挑戰遊戲
- 遊戲運營活動效果分析(三):區服合併分析遊戲
- 位元組跳動遊戲業務新動作,上線休閒遊戲分發平臺遊戲
- 動作遊戲打擊感到底從何而來?(上):幀與動作屬性遊戲
- 疊紙新作《戀與深空》今日首曝 全球首款次世代3D戀愛動作手遊預約開啟3D
- 《鬼泣5》、《只狼》:動作遊戲設計思考與分析遊戲設計
- 驅動篇——核心空間與核心模組
- 三個維度談遊戲服務,玩家的需求存在多大的價值空間?遊戲
- 歸納動作遊戲中的主動行為與被動行為遊戲
- 遊戲場景設計探究:空間潛意識遊戲
- 從作業系統角度看錶空間計算方式作業系統
- 拳拳到肉! 動作遊戲肉搏動作如何設計?遊戲
- 專業3D人物動畫製作DAZ Studio3D動畫
- 3D遊戲的照明設計理論(三):三點照明法的異端與誤區3D遊戲
- 使用THREE.js製作一款3D遊戲JS3D遊戲
- GOPATH 與工作空間Go
- 3D遊戲程式設計與設計4——遊戲物件與圖形基礎3D遊戲程式設計物件