一、太阳系仿真
- 制作九大行星的游戏实体
首先在网上找到九大行星对应的贴图材质,如下图所示:
- 创建空对象Solar,然后再创建九个球体,分别命名为九大行星
- 根据行星的大小调整各行星比例以及相对位置,并将贴图添加到对应的行星上,效果如下图所示
- 创建C#脚本PlantBehaviour,使行星运动起来,此处部分参考了师兄的代码,如下所示
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlantBehaviour : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
GameObject.Find("Sun").transform.Rotate(Vector3.up * Time.deltaTime * 5);
GameObject.Find("Mercury").transform.Rotate(Vector3.up * Time.deltaTime * 10000 / 58);
GameObject.Find("Mercury").transform.RotateAround(Vector3.zero, new Vector3(0.1f, 1, 0), 60 * Time.deltaTime);
GameObject.Find("Venus").transform.Rotate(Vector3.up * Time.deltaTime * 10000 / 243);
GameObject.Find("Venus").transform.RotateAround(Vector3.zero, new Vector3(0, 1, -0.1f), 55 * Time.deltaTime);
GameObject.Find("Earth").transform.Rotate(Vector3.up * Time.deltaTime * 10000);
GameObject.Find("Earth").transform.RotateAround(Vector3.zero, new Vector3(0, 1, 0), 50 * Time.deltaTime);
GameObject.Find("Mars").transform.Rotate(Vector3.up * Time.deltaTime * 10000);
GameObject.Find("Mars").transform.RotateAround(Vector3.zero, new Vector3(0.2f, 1, 0), 45 * Time.deltaTime);
GameObject.Find("Jupiter").transform.Rotate(Vector3.up * Time.deltaTime * 10000 / 0.3f);
GameObject.Find("Jupiter").transform.RotateAround(Vector3.zero, new Vector3(-0.1f, 2, 0), 35 * Time.deltaTime);
GameObject.Find("Saturn").transform.Rotate(Vector3.up * Time.deltaTime * 10000 / 0.4f);
GameObject.Find("Saturn").transform.RotateAround(Vector3.zero, new Vector3(0, 1, 0.2f), 20 * Time.deltaTime);
GameObject.Find("Uranus").transform.Rotate(Vector3.up * Time.deltaTime * 10000 / 0.6f);
GameObject.Find("Uranus").transform.RotateAround(Vector3.zero, new Vector3(0, 2, 0.1f), 15 * Time.deltaTime);
GameObject.Find("Nepture").transform.Rotate(Vector3.up * Time.deltaTime * 10000 / 0.7f);
GameObject.Find("Nepture").transform.RotateAround(Vector3.zero, new Vector3(-0.1f, 1, -0.1f), 10 * Time.deltaTime);
}
}
- 游戏运行效果
将脚本PlantBehaviour添加进Sun中,运行游戏,效果如下所示:
二、牧师与魔鬼
-
制作游戏对象
将游戏中所提及的游戏对象制作成预制件,其中包括三个牧师、三个魔鬼、一条小船、两岸以及河水。效果图如下所示:
-
用表格列出玩家动作表(规则表),如下表所示
动作 | 条件 | 结果 |
---|---|---|
点击角色(牧师或魔鬼) | 游戏未结束,角色在船上 | 角色上岸 |
点击角色(牧师或魔鬼) | 游戏未结束,角色在岸上,点击的角色与船在同一岸边 | 角色上船 |
点击船 | 游戏未结束,船上有至少一个角色 | 船移动到另一侧 |
点击重新开始 | 无 | 重新开始 |
- 脚本编写
此处参考了师兄之前的代码设计,同时也对代码中一些错误的地方进行修正,以及对于难理解的地方添加了注释。
- SSDirector
导演类,即Software School Director,负责把控全局,控制场景
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using interfacecon;
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;
}
}
- ModelCon
这个脚本实现了对于角色、船、河岸、的移动控制。
Move:这个类负责控制角色和船的移动。
public class Move : MonoBehaviour
{
float move_speed = 250;
int move_sign = 0;
Vector3 end_pos;
Vector3 middle_pos;
void Update()
{
if (move_sign == 1)
{
transform.position = Vector3.MoveTowards(transform.position, middle_pos, move_speed * Time.deltaTime);
if (transform.position == middle_pos)
move_sign = 2;
}
else if (move_sign == 2)
{
transform.position = Vector3.MoveTowards(transform.position, end_pos, move_speed * Time.deltaTime);
if (transform.position == end_pos)
move_sign = 0;
}
}
//设置移动的位置
public void MovePosition(Vector3 position)
{
//设置终点位置
end_pos = position;
if (position.y == transform.position.y)
{
move_sign = 2;
}
else if (position.y < transform.position.y)
{
middle_pos = new Vector3(position.x, transform.position.y, position.z);
}
else
{
middle_pos = new Vector3(transform.position.x, position.y, position.z);
}
move_sign = 1;
}
}
LandModel:这个类负责控制与河岸有关的动作,具体的有角色的上岸位置、下岸位置、船的离开和停靠、左岸和右岸的识别处理。具体解析看代码注释。
public class LandModel
{
GameObject land;
Vector3[] positions;
int land_sign;
RoleModel[] roles = new RoleModel[6];
public LandModel(string land_mark)
{
//修改地面标记以及对应位置
if (land_mark == "start")
{
//设置牧师和魔鬼站立的位置
positions = new Vector3[] {new Vector3(0,0.46F,0.49F), new Vector3(0,0.46F,0.735F), new Vector3(0,0.46F,0.988F),
new Vector3(0,0.46F,1.235F), new Vector3(0,0.46F,1.489F), new Vector3(0,0.46F,1.747F)};
land = Object.Instantiate(Resources.Load("Prefabs/Ground", typeof(GameObject)), new Vector3(0, 0, (float)1.090), Quaternion.identity) as GameObject;
land_sign = 1;
}
else if (land_mark == "end")
{
//设置牧师和魔鬼站立的位置
positions = new Vector3[] {new Vector3(0,0.46F,-4.25F), new Vector3(0,0.46F,-4.005F), new Vector3(0,0.46F,-3.752F),
new Vector3(0,0.46F,-3.505F), new Vector3(0,0.46F,-3.251F), new Vector3(0,0.46F,-2.993F)};
land = Object.Instantiate(Resources.Load("Prefabs/Ground", typeof(GameObject)), new Vector3(0, 0, (float)-3.65), 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;
}
}
RoleModel:这个类负责控制角色本身的动作,比如上船、上岸。
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)), new Vector3(0, 0, (float)2.090), Quaternion.Euler(0, -90, 0)) as GameObject;
role_sign = 0;
}
else
{
role = Object.Instantiate(Resources.Load("Prefabs/Devils", typeof(GameObject)), new Vector3(0, 0, (float)2.090), Quaternion.Euler(0, -90, 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;
}
}
BoatModel:这个类负责控制船的运动以及角色的上下船绑定。
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(-0.2F, 0.05F, -0.35F), Quaternion.identity) as GameObject;
//为游戏对象起名
boat.name = "boat";
//增加构件
move = boat.AddComponent(typeof(Move)) as Move;
click = boat.AddComponent(typeof(Click)) as Click;
//设置click对应的boat
click.SetBoat(this);
//设置船在起点的上船位置
start_empty_pos = new Vector3[] { new Vector3(-0.2F, 0.3F, -0.5F), new Vector3(-0.2F, 0.3F, -0.2F) };
//设置船在终点的上船位置
end_empty_pos = new Vector3[] { new Vector3(-0.2F, 0.3F, -2.4F), new Vector3(-0.2F, 0.3F, -2.2F) };
}
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(-0.2F, 0.05F, -0.35F));
boat_sign = 1;
}
else
{
move.MovePosition(new Vector3(-0.2F, 0.05F, -2.2F));
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;
}
}
- UserGUI
建立与用户的交互界面,比如按钮和标签。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using interfacecon;
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;
}
}
}
}
- InterfaceCon
这个脚本将所有的接口放在了同一命名空间下,这样方便了其他模块调用此命名空间。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using modelcon;
namespace interfacecon
{
public interface ISceneController
{
void LoadResources();
}
public interface IUserAction
{
void MoveBoat();
void Restart();
void MoveRole(RoleModel role);
int Check();
}
}
- Controller
控制器脚本,负责对场景中的具体对象进行操作,是游戏中各游戏对象行为改变的核心。它通过调用模型控制器以及接口的命名空间来调用其中实现的函数来达到控制的目的。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using modelcon;
using interfacecon;
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, (float)-0.151, (float)-1.251), 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;
}
}
- ClickCon
检测船和角色是否被点击。点击则触发接口中的动作,然后进入控制器进行对应的函数操作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using modelcon;
using interfacecon;
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);
}
}
- 游戏效果展示
游戏运行效果如下图所示:
视频展示:太阳系仿真和牧师与魔鬼演示视频
github地址:太阳系仿真和牧师与魔鬼
三、参考资料
[1]. unity官方文档
[2]. 师兄的博客(牧师与魔鬼)
[3]. 师兄的博客(太阳系仿真)