游戏对象运动的本质是游戏对象坐标位置,方向的变换。
实现基本抛物线的原理为:
水平方向上\(x=v_{0}t\)
竖直方向上\(y=at^{2}\)
实现的代码如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveOne : MonoBehaviour {
private float speed = 0.25f;
private float acceleration = 0.20f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
this.transform.position += Vector3.right * speed * Time.time;
this.transform.position += Vector3.up * acceleration * Time.time * Time.time;
}
}
效果图如下所示:
实现代码如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveOne : MonoBehaviour {
private float speed = 0.25f;
private float acceleration = 0.20f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
transform.Translate (transform.right * speed * Time.fixedTime, Space.World);
transform.Translate (transform.up * acceleration * Time.fixedTime * Time.fixedTime, Space.World);
}
}
该代码的效果和1.2.1的效果图是一样的,只是实现的方法不一样。
首先创建两个对象,Target1和Target2,两个对象的位置放置在很远的地方,然后让物体朝着两个目标个体移动,即可得到抛物线的运动轨迹。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveOne : MonoBehaviour {
private float speed = 2.0f;
public Transform target1;
public Transform target2;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
transform.position = Vector3.MoveTowards (transform.position, target1.position, Time.time);
transform.position = Vector3.MoveTowards (transform.position, target2.position, speed * Time.time * Time.time);
}
}
该代码的实现效果和1.2.1的效果一样
第三个步骤是通过实现一个太阳系来熟悉游戏对象运动的操作
首先我们先制作好样式,就是八大行星的壁纸贴图等等,相关的素材可以在网上找到,直接将其贴到相应创造出来的球体即可以
然后我们开始写公转和自转的脚本,因为八大行星都是绕太阳公转,而月球是绕地球公转的,所有八大行星都以太阳为中心点旋转,而月球则以地球为中心点旋转。
实现公转的代码如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Revolution : MonoBehaviour {
public Transform point;
public float speed;
private float ry,rz;
void Start () {
ry = Random.Range (0, 360);
rz = Random.Range (0, 360);
}
void Update () {
Vector3 axis = new Vector3(0,ry,rz);
this.transform.RotateAround(point.position,axis,speed * Time.deltaTime);
}
}
为了保证各大行星绕地球公转不在同一个法平面内,在开始程序的时候应该要随机产生一个向量,使得该行星能够绕这个轴公转
解决了公转的问题后,就要解决行星的自转问题了,行星的自转的脚本代码如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotation : MonoBehaviour {
private float speed = 0.5f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
this.transform.RotateAround (this.transform.position, Vector3.up, speed);
}
}
之后我们就基本能够制作出太阳系的轮廓了,为了能够增加效果,我们给行星运动的时候加上一些轨迹
Add Component => Trail Renderer
在这个框内调好Element 0的样式,Width宽度,Color颜色后就能够在行星运动的时候显示轨迹了
完成这步以后我们要给这个空间加上一个背景,加上背景的话,我们可以先创造一个立方体,然后把立方体的样式设置为我们的背景图案,然后把它的y和z属性设置得很大,而x属性设置得比较小,就能创造出一个背景来了
然后我们就能得到大致的效果图
游戏中提及的事物:牧师,恶魔,船,河流
游戏的规则表如下所示:
游戏的设计采用MVC模式,游戏中的所有GameObject就是Model,View包括UserGUI和ClickGUI,Controller包括FirstController->SceneController->MyCharcaterController,BoatController,CoastController.
导演Director即相应场景控制器的声明代码如下:
namespace Game {
/*单例模式*/
public class Director : System.Object {
private static Director _instance;
public SceneController currentSceneController { get; set; }
public static Director getInstance() {
if (_instance == null) {
_instance = new Director ();
}
return _instance;
}
}
/*场景控制器,生成对象,背景等等*/
public interface SceneController {
void loadResources ();
void loadBackground();
}
/*用户行为,即用户能够执行的操作*/
public interface UserAction {
void moveBoat();
void characterIsClicked(MyCharacterController characterCtrl);
void restart();
bool isGameOver();
}
/*The Function of move, 能够使物体运动的类*/
public class Moveable: MonoBehaviour {
readonly float move_speed = 20;
int moving_status;
// 0->not moving, 1->moving to middle, 2->moving to dest
Vector3 middle;
Vector3 dest;
void Update() {
if (moving_status == 1) {
transform.position = Vector3.MoveTowards (transform.position, middle, move_speed * Time.deltaTime);
if (transform.position == middle) {
moving_status = 2;
}
} else if (moving_status == 2) {
transform.position = Vector3.MoveTowards (transform.position, dest, move_speed * Time.deltaTime);
if (transform.position == dest) {
moving_status = 0;
}
}
}
/*通过设置目的地可以达到物体移动的效果*/
public void setDestination(Vector3 _dest) {
dest = _dest;
middle = _dest;
if (_dest.y == transform.position.y) {
// boat moving
moving_status = 2;
}
else if (_dest.y < transform.position.y) {
// character from coast to boat
middle.y = transform.position.y;
} else {
// character from boat to coast
middle.x = transform.position.x;
}
moving_status = 1;
}
public void reset() {
moving_status = 0;
}
}
}
相应的对象由相应的Controller实现,下面创建了一个namespace,用于聚集各类Controller类
BackgroundController用于控制生成背景图案
public class BackgroundController
{
readonly GameObject background;
public BackgroundController()
{
background = Object.Instantiate(Resources.Load("Perfabs/Background", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
}
public void setName(string _name)
{
background.name = _name;
}
public void setPosition(Vector3 background_position)
{
background.transform.position = background_position;
}
public string getName()
{
return background.name;
}
}
MyCharacterController用于控制牧师和魔鬼对象
public class MyCharacterController
{
readonly GameObject character;
readonly Moveable moveableScript;
readonly ClickGUI clickGUI;
readonly int characterType;
// 0->priest, 1->devil
bool _isOnBoat;
CoastController coastController;
public MyCharacterController(string which_character)
{
if (which_character == "priest")
{
character = Object.Instantiate(Resources.Load("Perfabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
characterType = 0;
}
else
{
character = Object.Instantiate(Resources.Load("Perfabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
characterType = 1;
}
moveableScript = character.AddComponent(typeof(Moveable)) as Moveable;
clickGUI = character.AddComponent(typeof(ClickGUI)) as ClickGUI;
clickGUI.setController(this);
}
public void setName(string name)
{
character.name = name;
}
public void setPosition(Vector3 pos)
{
character.transform.position = pos;
}
public void moveToPosition(Vector3 destination)
{
moveableScript.setDestination(destination);
}
public int getType()
{
return characterType;
}
public string getName()
{
return character.name;
}
public void getOnBoat(BoatController boatCtrl)
{
coastController = null;
character.transform.parent = boatCtrl.getGameobj().transform;
_isOnBoat = true;
}
public void getOnCoast(CoastController coastCtrl)
{
coastController = coastCtrl;
character.transform.parent = null;
_isOnBoat = false;
}
public bool isOnBoat()
{
return _isOnBoat;
}
public CoastController getCoastController()
{
return coastController;
}
public void reset()
{
moveableScript.reset();
coastController = (Director.getInstance().currentSceneController as FirstController).fromCoast;
getOnCoast(coastController);
setPosition(coastController.getEmptyPosition());
coastController.getOnCoast(this);
}
}
BoatController用于控制船的对象
public class BoatController
{
readonly GameObject boat;
readonly Moveable moveableScript;
readonly Vector3 fromPosition = new Vector3(3.0f, 0.8f, 0);
readonly Vector3 toPosition = new Vector3(-3.0f, 0.8f, 0);
readonly Vector3[] from_positions;
readonly Vector3[] to_positions;
int to_or_from;
// to->-1; from->1
MyCharacterController[] passenger = new MyCharacterController[2];
public BoatController()
{
to_or_from = 1;
from_positions = new Vector3[] { new Vector3(2.5F, 2.0F, 0), new Vector3(3.5F, 2.0F, 0) };
to_positions = new Vector3[] { new Vector3(-2.5F, 2.0F, 0), new Vector3(-3.5F, 2.0F, 0) };
boat = Object.Instantiate(Resources.Load("Perfabs/Boat", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;
boat.name = "boat";
moveableScript = boat.AddComponent(typeof(Moveable)) as Moveable;
boat.AddComponent(typeof(ClickGUI));
}
public void Move()
{
if (to_or_from == -1)
{
moveableScript.setDestination(fromPosition);
to_or_from = 1;
}
else
{
moveableScript.setDestination(toPosition);
to_or_from = -1;
}
}
public int getEmptyIndex()
{
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] == null)
{
return i;
}
}
return -1;
}
public bool isEmpty()
{
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] != null)
{
return false;
}
}
return true;
}
public Vector3 getEmptyPosition()
{
Vector3 pos;
int emptyIndex = getEmptyIndex();
if (to_or_from == -1)
{
pos = to_positions[emptyIndex];
}
else
{
pos = from_positions[emptyIndex];
}
return pos;
}
public void GetOnBoat(MyCharacterController characterCtrl)
{
int index = getEmptyIndex();
passenger[index] = characterCtrl;
}
public MyCharacterController GetOffBoat(string passenger_name)
{
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] != null && passenger[i].getName() == passenger_name)
{
MyCharacterController charactorCtrl = passenger[i];
passenger[i] = null;
return charactorCtrl;
}
}
return null;
}
public GameObject getGameobj()
{
return boat;
}
public int get_to_or_from()
{
return to_or_from;
}
public int[] getCharacterNum()
{
int[] count = { 0, 0 };
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] == null)
continue;
if (passenger[i].getType() == 0)
{
count[0]++;
}
else
{
count[1]++;
}
}
return count;
}
public void reset()
{
moveableScript.reset();
if (to_or_from == -1)
{
Move();
}
passenger = new MyCharacterController[2];
}
}
CoastController用于控制河岸对象
public class CoastController
{
readonly GameObject coast;
readonly Vector3 from_pos = new Vector3(9, -2, 0);
readonly Vector3 to_pos = new Vector3(-9, -2, 0);
readonly Vector3[] positions;
readonly int to_or_from; // to->-1, from->1
// change frequently
MyCharacterController[] passengerPlaner;
public CoastController(string _to_or_from)
{
positions = new Vector3[] {
new Vector3(5.4F,2.4F,0),
new Vector3(6.6F,2.4F,0),
new Vector3(7.8F,2.4F,0),
new Vector3(9.0F,2.4F,0),
new Vector3(10.2F,2.4F,0),
new Vector3(11.4F,2.4F,0)};
passengerPlaner = new MyCharacterController[6];
if (_to_or_from == "from")
{
coast = Object.Instantiate(Resources.Load("Perfabs/Stone", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;
coast.name = "from";
to_or_from = 1;
}
else
{
coast = Object.Instantiate(Resources.Load("Perfabs/Stone", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;
coast.name = "to";
to_or_from = -1;
}
}
public int getEmptyIndex()
{
for (int i = 0; i < passengerPlaner.Length; i++)
{
if (passengerPlaner[i] == null)
{
return i;
}
}
return -1;
}
public Vector3 getEmptyPosition()
{
Vector3 pos = positions[getEmptyIndex()];
pos.x *= to_or_from;
return pos;
}
public void getOnCoast(MyCharacterController characterCtrl)
{
int index = getEmptyIndex();
passengerPlaner[index] = characterCtrl;
}
public MyCharacterController getOffCoast(string passenger_name)
{ // 0->priest, 1->devil
for (int i = 0; i < passengerPlaner.Length; i++)
{
if (passengerPlaner[i] != null && passengerPlaner[i].getName() == passenger_name)
{
MyCharacterController charactorCtrl = passengerPlaner[i];
passengerPlaner[i] = null;
return charactorCtrl;
}
}
return null;
}
public int get_to_or_from()
{
return to_or_from;
}
public int[] getCharacterNum()
{
int[] count = { 0, 0 };
for (int i = 0; i < passengerPlaner.Length; i++)
{
if (passengerPlaner[i] == null)
continue;
if (passengerPlaner[i].getType() == 0)
{ // 0->priest, 1->devil
count[0]++;
}
else
{
count[1]++;
}
}
return count;
}
public void reset()
{
passengerPlaner = new MyCharacterController[6];
}
}
最后是FirstController和UserGUI和ClickGUI的代码
public class FirstController : MonoBehaviour, SceneController, UserAction {
/*动态常量 readonly*/
readonly Vector3 water_pos = new Vector3(0,-2.0F,0);
readonly Vector3 background_pos = new Vector3(0, 0, 20);
UserGUI userGUI;
public BackgroundController Background;
public CoastController fromCoast;
public CoastController toCoast;
public BoatController boat;
private MyCharacterController[] characters;
void Awake() {
Director director = Director.getInstance ();
director.currentSceneController = this;
userGUI = gameObject.AddComponent () as UserGUI;
characters = new MyCharacterController[6];
loadBackground();
loadResources ();
}
public void loadBackground()
{
/*创造背景*/
Background = new BackgroundController();
Background.setPosition(background_pos);
}
public void loadResources() {
/*在Resources文件夹中加载Water,只有GameObject类型的才被加载,加载到water_pos的位置,无旋转,完全对齐于世界*/
GameObject water = Instantiate (Resources.Load ("Perfabs/Water", typeof(GameObject)), water_pos, Quaternion.identity, null) as GameObject;
water.name = "water";
/*创造两个对岸*/
fromCoast = new CoastController ("from");
toCoast = new CoastController ("to");
/*创造船*/
boat = new BoatController ();
/*创造牧师与魔鬼*/
loadCharacter ();
}
private void loadCharacter() {
for (int i = 0; i < 3; i++) {
MyCharacterController Character = new MyCharacterController ("priest");
Character.setName("priest" + i);
Character.setPosition (fromCoast.getEmptyPosition ());
Character.getOnCoast (fromCoast);
fromCoast.getOnCoast (Character);
characters [i] = Character;
}
for (int i = 0; i < 3; i++) {
MyCharacterController Character = new MyCharacterController ("devil");
Character.setName("devil" + i);
Character.setPosition (fromCoast.getEmptyPosition ());
Character.getOnCoast (fromCoast);
fromCoast.getOnCoast (Character);
characters [i+3] = Character;
}
}
public void moveBoat() {
if (boat.isEmpty ()) return;
boat.Move ();
userGUI.status = check_game_over ();
}
public void characterIsClicked(MyCharacterController characterCtrl) {
if (characterCtrl.isOnBoat ()) {
CoastController whichCoast;
if (boat.get_to_or_from () == -1) { // to->-1; from->1
whichCoast = toCoast;
} else {
whichCoast = fromCoast;
}
boat.GetOffBoat (characterCtrl.getName());
characterCtrl.moveToPosition (whichCoast.getEmptyPosition ());
characterCtrl.getOnCoast (whichCoast);
whichCoast.getOnCoast (characterCtrl);
} else {
CoastController whichCoast = characterCtrl.getCoastController ();
if (boat.getEmptyIndex () == -1) {
return;
}
if (whichCoast.get_to_or_from () != boat.get_to_or_from ())
// boat is not on the side of character
return;
whichCoast.getOffCoast(characterCtrl.getName());
characterCtrl.moveToPosition (boat.getEmptyPosition());
characterCtrl.getOnBoat (boat);
boat.GetOnBoat (characterCtrl);
}
userGUI.status = check_game_over ();
}
public bool isGameOver()
{
return check_game_over() != 0;
}
int check_game_over() {
int from_priest = 0;
int from_devil = 0;
int to_priest = 0;
int to_devil = 0;
int[] fromCount = fromCoast.getCharacterNum ();
from_priest += fromCount[0];
from_devil += fromCount[1];
int[] toCount = toCoast.getCharacterNum ();
to_priest += toCount[0];
to_devil += toCount[1];
if (to_priest + to_devil == 6)
return 2;
int[] boatCount = boat.getCharacterNum ();
if (boat.get_to_or_from () == -1)
{
to_priest += boatCount[0];
to_devil += boatCount[1];
}
else
{
from_priest += boatCount[0];
from_devil += boatCount[1];
}
if (from_priest < from_devil && from_priest > 0)
{
return 1;
}
if (to_priest < to_devil && to_priest > 0)
{
return 1;
}
return 0;
}
public void restart() {
boat.reset ();
fromCoast.reset ();
toCoast.reset ();
for (int i = 0; i < characters.Length; i++) {
characters [i].reset ();
}
}
}
ClickGUI和UserGUI的代码如下:
public class UserGUI : MonoBehaviour {
private UserAction Player;
public int status = 0;
GUIStyle style;
GUIStyle Button;
void Start() {
Player = Director.getInstance ().currentSceneController as UserAction;
style = new GUIStyle();
style.fontSize = 40;
style.alignment = TextAnchor.MiddleCenter;
Button = new GUIStyle("button");
Button.fontSize = 30;
}
void OnGUI() {
if (status == 1) {
GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-85, 100, 50), "Gameover!", style);
if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2, 140, 70), "Restart", Button)) {
status = 0;
Player.restart ();
}
} else if(status == 2) {
GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-85, 100, 50), "You win!", style);
if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2, 140, 70), "Restart", Button)) {
status = 0;
Player.restart ();
}
}
if (GUI.Button(new Rect(0, 0, 150, 50), "Reset"))
{
Player.restart();
}
}
}
public class ClickGUI : MonoBehaviour {
UserAction Player;
MyCharacterController characterController;
public void setController(MyCharacterController characterCtrl) {
characterController = characterCtrl;
}
void Start() {
Player = Director.getInstance ().currentSceneController as UserAction;
}
void OnMouseDown() {
if(!Player.isGameOver())
{
if (gameObject.name == "boat")
{
Player.moveBoat();
}
else
{
Player.characterIsClicked(characterController);
}
}
}
}
最后的效果图如下所示,虽然大部分代码都是参考师兄的....但是感觉还是在搬砖过程中了解到不少东西....