1、简答并用程序验证【建议做】
游戏对象运动的本质是什么?
游戏对象运动的本质是对游戏对象的位置、大小、角度属性数值的变化。
请用三种方法以上方法,实现物体的抛物线运动。(如,修改Transform属性,使用向量Vector3的方法…)
假定空气阻力可以忽略,水平方向上初始速度为1,做匀速运动,竖直方向上易知为匀加速运动。
修改transform属性:单位时间内,水平位移增加vx*t,竖直速度增加g,竖直位移增加vy。
public class move: MonoBehaviour { int vx, vy, g = 10; void Start () { vx = 1; vy = 0; } void Update () { vy += g; transform.position += Vector3.right * Time.deltaTime * vx; transform.position += Vector3.down * Time.deltaTime * vy; } }
使用vector3的方法:使用向量v表示单位时间内变化的位移,s=(水平位移,-竖直位移的大小,0)。
public class move: MonoBehaviour { int vx, vy, g = 10; void Start () { vx = 1; vy = 0; } void Update () { vy += g; Vector3 s = new Vector3(Time.deltaTime * vx, -Time.deltaTime * vy, 0); transform.position += s; } }
使用translate方法,与上面的类似,最后一步改为translate(s)。
public class move: MonoBehaviour { int vx, vy, g = 10; void Start () { vx = 1; vy = 0; } void Update () { vy += g; Vector3 s = new Vector3(Time.deltaTime * vx, -Time.deltaTime * vy, 0); transform.Translate(s); } }
写一个程序,实现一个完整的太阳系, 其他星球围绕太阳的转速必须不一样,且不在一个法平面上。
创建十个球体(太阳、八大行星、月球),并贴上图片。
自转函数,让恒星与行星自转。将脚本拖入到恒星与行星中。
public class Rotation : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { this.transform.RotateAround(this.transform.position, Vector3.up, 20*Time.deltaTime); } }
公转函数,通过随机函数生成公转法向量,公转速度设置为水星30,金星15,地球10,火星5,木星、土星、天王星、海王星设为1。将脚本拖入到每一个行星中。
public class OR : MonoBehaviour { public Transform origin; float ry, rx; public float speed; // Use this for initialization void Start() { rx = Random.Range(1, 3); ry = Random.Range(1, 3); } // Update is called once per frame void Update() { Vector3 v = new Vector3(rx, ry, 0); this.transform.RotateAround(origin.position, v, speed * Time.deltaTime); } }
为月球设置公转函数,由于月球是地球的卫星,所以将月球设为地球的子类。月球的速度设为120。
public Transform origin; public float speed; // Use this for initialization void Start() { } // Update is called once per frame void Update() { this.transform.RotateAround(this.transform.parent.position, Vector3.up, speed * Time.deltaTime); }
为每一个星球设置轨道组件,时间尽量设置长,方便看见完整轨道。
下面为太阳系设置背景。首先新建一个camera与一个canvas,并在canvas中新建一个image,将我们需要添加的图片的texture type改为sprite。
接着任意选中一个对象,在其inspector中点击layer,新建一个layer,将camera与canvas放入,并调整camera与canvas的属性。
主相机也需要调整相应属性,在Culling Mask中去掉background也就是我们新建的图层。
最后设置背景图片,将类型设置为平铺,长宽尽可能设置大。
PS:后来发现可以使用天空盒实现背景功能!上文的方法显得好麻烦啊......于是换成天空盒实现。首先在Assets文件夹中新建一个SkyBox文件夹,放入需要的背景图片。
将图片的Inspector栏中的wrap mode设为clamp,解决图片衔接过渡不自然的问题。
新建一个材质球,在shader处选择skybox->6,将准备好的背景图附上去,就完成了天空盒的设置。window--->Renderning---->Lighting Settings,可以看到Lighting界面,然后在environment属性栏中给SkyBox属性,选择刚才制作的天空盒,应用后效果如下:
一个小问题:使用天空盒时发现星球运动会有残影
需要回到主相机,将clear flags的depth only改为skybox,就正常显示了。
2.编程实践:完成牧师与魔鬼
玩家动作表格
玩家动作
条件
上船
船上有空位且点击对象
下船
船上有对象且点击对象
开船
点击船且船上有对象
首先创建Resources-Perfabs文件夹,创建预设体。这里采用MineCraft风格,牧师贴图为史蒂夫,魔鬼贴图为苦力怕。先在Scene中create一个cube,贴好图后拖进Perfabs文件中即可成为预设。
创建一个天空盒,方法如上文太阳系中所述,背景贴图同样采取MC风格。
创建Script文件夹,存放需要用的的脚本文件。游戏采取MVC架构,即model-controller-view三部分。
SSDirector:使用单例模式保证导演实例有且只有一个,掌控着场景的加载、切换等,也可以控制游戏暂停、结束等等。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Interfaces; public class SSDirector : System.Object { private static SSDirector _instance; public ISceneController currentScenceController { get; set; } public bool running { get; set; } public static SSDirector getInstance() { if (_instance == null) { _instance = new SSDirector(); } return _instance; } }
Interfaces:创建ISceneController和IUserAction两个接口,ISceneController负责场景的加载,IUserAction负责响应用户的操作,如点击鼠标使游戏对象移动。
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Interfaces { public interface ISceneController { void LoadResources(); } public interface IUserAction { void MoveBoat(); void ObjectIsClicked(GameObjects characterCtrl); void Restart(); } }
FirstController:负责实例化ISceneController和IUserAction,实现资源的加载和响应用户操作。LoadResources实例化了水面与水底岩石,并且为牧师与恶魔设置名字、位置属性。ObjectIsClicked响应鼠标点击操作,首先判断对象是否在船上,如果在通过对状态(1为出发岸,-1为目的岸)的判断来上下岸;如果不在船上则在船上寻找空位置上船。MoveBoat响应船的移动。Check判断游戏的状态为继续进行、游戏胜利还是游戏结束。Restart是在游戏失败结束后重新开始,对资源、对象重新加载。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Interfaces; public class FirstController : MonoBehaviour, ISceneController, IUserAction { InteracteGUI UserGUI; public CoastController fromCoast; public CoastController toCoast; public BoatController boat; private GameObjects[] GameObjects; void Awake() { SSDirector director = SSDirector.getInstance(); director.currentScenceController = this; UserGUI = gameObject.AddComponent<InteracteGUI>() as InteracteGUI; GameObjects = new GameObjects[6]; LoadResources(); } public void LoadResources() { fromCoast = new CoastController("from"); toCoast = new CoastController("to"); boat = new BoatController(); GameObject water = Instantiate(Resources.Load("Perfabs/river", typeof(GameObject)), new Vector3(0, -1, 0), Quaternion.identity, null) as GameObject; GameObject rock = Instantiate(Resources.Load("Perfabs/rock", typeof(GameObject)), new Vector3(0, -2, 0), Quaternion.identity, null) as GameObject; water.name = "water"; for (int i = 0; i < 3; i++) { GameObjects s = new GameObjects("priest"); s.setName("priest" + i); s.setPosition(fromCoast.getEmptyPosition()); s.getOnCoast(fromCoast); fromCoast.getOnCoast(s); GameObjects[i] = s; } for (int i = 0; i < 3; i++) { GameObjects s = new GameObjects("devil"); s.setName("devil" + i); s.setPosition(fromCoast.getEmptyPosition()); s.getOnCoast(fromCoast); fromCoast.getOnCoast(s); GameObjects[i + 3] = s; } } public void ObjectIsClicked(GameObjects Objects) { if (Objects.isOnBoat()) { CoastController whichCoast; if (boat.get_State() == -1) { whichCoast = toCoast; } else { whichCoast = fromCoast; } boat.GetOffBoat(Objects.getName()); Objects.moveToPosition(whichCoast.getEmptyPosition()); Objects.getOnCoast(whichCoast); whichCoast.getOnCoast(Objects); } else { CoastController whichCoast = Objects.getCoastController(); if (boat.getEmptyIndex() == -1) { return; } if (whichCoast.get_State() != boat.get_State()) return; whichCoast.getOffCoast(Objects.getName()); Objects.moveToPosition(boat.getEmptyPosition()); Objects.getOnBoat(boat); boat.GetOnBoat(Objects); } UserGUI.SetState = Check(); } public void MoveBoat() { if (boat.isEmpty()) return; boat.Move(); UserGUI.SetState = Check(); } int Check() { int from_priest = 0; int from_devil = 0; int to_priest = 0; int to_devil = 0; int[] fromCount = fromCoast.GetobjectsNumber(); from_priest += fromCount[0]; from_devil += fromCount[1]; int[] toCount = toCoast.GetobjectsNumber(); to_priest += toCount[0]; to_devil += toCount[1]; if (to_priest + to_devil == 6) return 2; int[] boatCount = boat.GetobjectsNumber(); if (boat.get_State() == -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 < GameObjects.Length; i++) { GameObjects[i].reset(); } } }
IUserGUI:建立用户交互界面,如按钮。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Interfaces; public class IUserGUI : MonoBehaviour { IUserAction UserAcotionController; public int SetState { get { return GameState; } set { GameState = value; } } static int GameState = 0; void Start() { UserAcotionController = SSDirector.getInstance().currentScenceController as IUserAction; } private void OnGUI() { GUI.skin.button.fontSize = 25; GUIStyle tstyle = new GUIStyle(); tstyle.fontSize = 30; tstyle.normal.textColor = new Color(255, 0, 0); if (GameState == 1) { GUI.Label(new Rect(Screen.width / 2 - 80, Screen.height / 2-100, 160, 80), "Game over!",tstyle); if (GUI.Button(new Rect(Screen.width / 2 - 80, Screen.height / 2, 160, 80), "Restart")) { GameState = 0; UserAcotionController.Restart(); } } else if (GameState == 2) { GUI.Label(new Rect(Screen.width / 2 - 80, Screen.height / 2 - 100, 160, 80), "You win!",tstyle); if (GUI.Button(new Rect(Screen.width / 2 - 80, Screen.height / 2, 160, 80), "Restart")) { GameState = 0; UserAcotionController.Restart(); } } } } public class ClickGUI : MonoBehaviour { IUserAction UserAcotionController; GameObjects GameObjectsInScene; public void setController(GameObjects characterCtrl) { GameObjectsInScene = characterCtrl; } void Start() { UserAcotionController = SSDirector.getInstance().currentScenceController as IUserAction; } void OnMouseDown() { if (gameObject.name == "boat") { UserAcotionController.MoveBoat(); } else { UserAcotionController.ObjectIsClicked(GameObjectsInScene); } } }
GameObjects:对游戏对象的初始化。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameObjects { readonly GameObject Instance; readonly Move Move; readonly ClickGUI clickGUI; readonly int characterType; // 0->priest, 1->devil bool _isOnBoat = false; CoastController coastController; public GameObjects(string Type) { if (Type == "priest") { Instance = Object.Instantiate(Resources.Load("Perfabs/priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject; characterType = 0; } else { Instance = Object.Instantiate(Resources.Load("Perfabs/devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject; characterType = 1; } Move = Instance.AddComponent(typeof(Move)) as Move; clickGUI = Instance.AddComponent(typeof(ClickGUI)) as ClickGUI; clickGUI.setController(this); } public void setName(string name) { Instance.name = name; } public void setPosition(Vector3 pos) { Instance.transform.position = pos; } public void moveToPosition(Vector3 destination) { Move.SetDestination(destination); } public int getType() { // 0->priest, 1->devil return characterType; } public string getName() { return Instance.name; } public void getOnBoat(BoatController boatCtrl) { coastController = null; Instance.transform.parent = boatCtrl.getGameobj().transform; _isOnBoat = true; } public void getOnCoast(CoastController coastCtrl) { coastController = coastCtrl; Instance.transform.parent = null; _isOnBoat = false; } public bool isOnBoat() { return _isOnBoat; } public CoastController getCoastController() { return coastController; } public void reset() { Move.Reset(); coastController = (SSDirector.getInstance().currentScenceController as FirstController).fromCoast; getOnCoast(coastController); setPosition(coastController.getEmptyPosition()); coastController.getOnCoast(this); } }
Move:实现游戏对象的移动(船的移动与游戏角色的上下船)。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Move : MonoBehaviour { readonly float Speed = 20; Vector3 Target; Vector3 Middle; int Move_State = 0; // 0->no need to move, 1->object moving , 2->boat moving to dest bool To_Middle = true; void Update() { if (Move_State == 1) { if (To_Middle) { transform.position = Vector3.MoveTowards(transform.position, Middle, Speed * Time.deltaTime); if (transform.position == Middle) To_Middle = false; } else { transform.position = Vector3.MoveTowards(transform.position, Target, Speed * Time.deltaTime); if (transform.position == Target) Move_State = 0; } } else if (Move_State == 2) { transform.position = Vector3.MoveTowards(transform.position, Target, Speed * Time.deltaTime); if (transform.position == Target) { To_Middle = true; Move_State = 0; } } } public void SetDestination(Vector3 Position) { if (Move_State != 0) return; Target = Middle = Position; To_Middle = true; if (transform.position.y == Target.y) { Move_State = 2; } else { Move_State = 1; if (transform.position.y < Target.y) { Middle.x = transform.position.x; } else if (transform.position.y > Target.y) { Middle.y = transform.position.y; } } } public void Reset() { Move_State = 0; To_Middle = true; } }
BoatController&CoastController:船的河岸的控制器,控制船和河岸的初始位置,以及牧师与魔鬼的初始位置、上下船位置。
public BoatController() { State = 1; from_positions = new Vector3[] { new Vector3(3.5F, 1.5F, 0), new Vector3(4.5F, 1.5F, 0) }; to_positions = new Vector3[] { new Vector3(-4.5F, 1.5F, 0), new Vector3(-3.5F, 1.5F, 0) }; boat = Object.Instantiate(Resources.Load("Perfabs/boat", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject; boat.name = "boat"; Moving = boat.AddComponent(typeof(Move)) as Move; boat.AddComponent(typeof(ClickGUI)); } public CoastController(string _State) { positions = new Vector3[] {new Vector3(7.5F,2F,0), new Vector3(8.5F,2F,0), new Vector3(9.5F,2F,0), new Vector3(10.5F,2F,0), new Vector3(11.5F,2F,0), new Vector3(12.5F,2F,0)}; passengerPlaner = new GameObjects[6]; if (_State == "from") { land = Object.Instantiate(Resources.Load("Perfabs/land", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject; soil = Object.Instantiate(Resources.Load("Perfabs/soil", typeof(GameObject)), new Vector3(8, -1.5F, 0), Quaternion.identity, null) as GameObject; land.name = "from"; State = 1; } else { land = Object.Instantiate(Resources.Load("Perfabs/land", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject; soil = Object.Instantiate(Resources.Load("Perfabs/soil", typeof(GameObject)), new Vector3(-8, -1.5F, 0), Quaternion.identity, null) as GameObject; land.name = "to"; State = -1; } }
新建一个空的GameObject,将FirstController挂载上去,点击运行。
