3d游戏第三次作业

1、简答并用程序验证【建议做】

游戏对象运动的本质是什么?

是游戏物体随着时间的变化而引起空间的变化。具体来说就是当刷新帧的时候,游戏物体transorm参数变化,具体包括position和rotation的变化。

请用三种方法以上方法,实现物体的抛物线运动。(如,修改Transform属性,使用向量Vector3的方法…)

抛物线运动是水平匀速,竖直方向加速。第一种方法可以直接在update修改每一帧的时候直接更新transform的属性:


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{
    private float speedx = 5;
    private float speedy = 0;
    private float g = 10;
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {      
        transform.position += Vector3.right * speedx * Time.deltaTime;
        transform.position += Vector3.down * speedy * Time.deltaTime;
        speedy += g * Time.deltaTime;
    }
}

也可以使用Vector3来间接修改(在上面代码基础上修改如下代码段):

void Update()
    {
        speedy += g * Time.deltaTime;
        Vector3 temp = new Vector3(speedx * Time.deltaTime, -speedy * Time.deltaTime, 0);
        transform.position += temp;
    }

还可以修改物体的重力属性来实现:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{
    void Start()
    {
        gameObject.AddComponent<Rigidbody>();
        gameObject.GetComponent<Rigidbody>().velocity = new Vector3(1, 0, 0);
    }

    void Update() { }
}

写一个程序,实现一个完整的太阳系, 其他星球围绕太阳的转速必须不一样,且不在一个法平面上。

创建太阳系:
create solarSystem
脚本:

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;


    void Start() { }

    void Update()
    {

        sun.Rotate(Vector3.up,  Time.deltaTime * 10);

        mercury.RotateAround(this.transform.position, new Vector3(1, 10, 0), 50 * Time.deltaTime);
        mercury.Rotate(Vector3.up,  Time.deltaTime * 100);

        venus.RotateAround(this.transform.position, new Vector3(1, 13, 0), 40 * Time.deltaTime);
        venus.Rotate(Vector3.up,  Time.deltaTime * 100);

        earth.RotateAround(this.transform.position, new Vector3(1, 15, 0), 35 * Time.deltaTime);
        earth.Rotate(Vector3.up,  Time.deltaTime * 100);

        mars.RotateAround(this.transform.position, new Vector3(2, 10, 0), 30 * Time.deltaTime);
        mars.Rotate(Vector3.up,  Time.deltaTime * 100);

        jupiter.RotateAround(this.transform.position, new Vector3(2, 13, 0), 20 * Time.deltaTime);
        jupiter.Rotate(Vector3.up,  Time.deltaTime * 100);

        saturn.RotateAround(this.transform.position, new Vector3(2, 15, 0), 15 * Time.deltaTime);
        saturn.Rotate(Vector3.up,  Time.deltaTime * 100);

        uranus.RotateAround(this.transform.position, new Vector3(3, 10, 0), 10 * Time.deltaTime);
        uranus.Rotate(Vector3.up,  Time.deltaTime * 100);

        neptune.RotateAround(this.transform.position, new Vector3(3, 15, 0), 9 * Time.deltaTime);
        neptune.Rotate(Vector3.up,  Time.deltaTime * 100);
    }
}

运行效果

2、编程实践

阅读以下游戏脚本

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 结构程序
  • 注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!

游戏中的对象:牧师、魔鬼、小船、河岸、河水
玩家动作表(规则表)

动作条件结果
点击岸上的牧师或者魔鬼船未满并且在岸边点击的牧师或者魔鬼上船
点击小船船上有牧师或者魔鬼移动到另一岸
点击船上的牧师或魔鬼船在岸边牧师或者魔鬼上岸

创建预制,将牧师做成浅黄方块,恶魔做成深红方块
预制
见上图,资源被整合成材料、预制和脚本三个文件夹。

  • 运行前的游戏对象
    运行前
  • 运行后的游戏对象
    在这里插入图片描述
    使用MVC架构:
  • 模型(Model):数据对象及关系
    所有的GameObject
  • 控制器(Controller):接受用户事件,控制模型的变化
    Director类,FirstController类,Move类,RolesController类,CoastController类,BoatController类
  • 界面(View):显示模型,将人机交互事件交给控制器处理
    UserGUI和ClickGUI脚本

主要的一些类的解释:

Director

获取当前游戏的场景
控制场景运行、切换、入栈与出栈
暂停、恢复、退出
管理游戏全局状态
设定游戏的配置
设定游戏全局视图

	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;
		}
	}

可以看到无论何时调用getInstance()都会得到同一个实例,这保证了导演类的唯一性,也能保证导演类控制场记的作用。

四个控制类

Moveable
 控制游戏物体的移动
RolesController
 控制牧师或者魔鬼
CoastController
 控制河岸
BoatController
 控制小船

	public class Moveable: MonoBehaviour {
		
		readonly float move_speed = 30;

		int moving_status;	// 0->not moving, 1->moving end middle, 2->moving end dest
		Vector3 dest;
		Vector3 middle;

		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)
				moving_status = 2;
		
			else if (_dest.y < transform.position.y) 
				middle.y = transform.position.y;
			else 						
				middle.x = transform.position.x;
			
			moving_status = 1;
		}

		public void reset() {
			moving_status = 0;
		}
	}


	public class RolesController {
		readonly GameObject character;
		readonly Moveable moveableScript;
		readonly ClickGUI clickGUI;
		readonly int RoleType;	// 0->priest, 1->devil

		// change frequently
		bool _isOnBoat;
		CoastController coastController;


		public RolesController(string which_character) {
			
			if (which_character == "priest") {
				character = Object.Instantiate (Resources.Load ("Perfabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
				RoleType = 0;
			} else {
				character = Object.Instantiate (Resources.Load ("Perfabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
				RoleType = 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() {	// 0->priest, 1->devil
			return RoleType;
		}

		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).bankbegin;
			getOnCoast (coastController);
			setPosition (coastController.getEmptyPosition ());
			coastController.getOnCoast (this);
		}
	}


	public class CoastController {
		readonly GameObject coast;
		readonly Vector3 from_pos = new Vector3(9,1,0);
		readonly Vector3 to_pos = new Vector3(-9,1,0);
		readonly Vector3[] positions;
		readonly int postion;	// end->-1, begin->1

		// change frequently
		RolesController[] passengerPlaner;

		public CoastController(string _postion) {
			positions = new Vector3[] {
                new Vector3(6.5F,2.25F,0), 
                new Vector3(7.5F,2.25F,0), 
                new Vector3(8.5F,2.25F,0), 
				new Vector3(9.5F,2.25F,0), 
                new Vector3(10.5F,2.25F,0), 
                new Vector3(11.5F,2.25F,0)};

			passengerPlaner = new RolesController[6];

			if (_postion == "begin") {
				coast = Object.Instantiate (Resources.Load ("Perfabs/Bank", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;
				coast.name = "begin";
				postion = 1;
			} else {
				coast = Object.Instantiate (Resources.Load ("Perfabs/Bank", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;
				coast.name = "end";
				postion = -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 *= postion;
			return pos;
		}

		public void getOnCoast(RolesController characterCtrl) {
			int index = getEmptyIndex ();
			passengerPlaner [index] = characterCtrl;
		}

		public RolesController 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) {
					RolesController charactorCtrl = passengerPlaner [i];
					passengerPlaner [i] = null;
					return charactorCtrl;
				}
			
			Debug.Log ("cant find passenger on coast: " + passenger_name);
			return null;
		}

		public int where() {
			return postion;
		}

		public int[] getRoleNum() {
			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 RolesController[6];
		}
	}


	public class BoatController {
		readonly GameObject boat;
		readonly Moveable moveableScript;
		readonly Vector3 fromPosition = new Vector3 (5, 1, 0);
		readonly Vector3 toPosition = new Vector3 (-5, 1, 0);
		readonly Vector3[] beginPostion;
		readonly Vector3[] endPostion;

		// change frequently
		int postion; // end->-1; begin->1
		RolesController[] passenger = new RolesController[2];

		public BoatController() {
			postion = 1;

			beginPostion = new Vector3[] { new Vector3 (4.5F, 1.5F, 0), new Vector3 (5.5F, 1.5F, 0) };
			endPostion = new Vector3[] { new Vector3 (-5.5F, 1.5F, 0), new Vector3 (-4.5F, 1.5F, 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 (postion == -1) {
				moveableScript.setDestination(fromPosition);
				postion = 1;
			} else {
				moveableScript.setDestination(toPosition);
				postion = -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 (postion == -1) 
				pos = endPostion[emptyIndex];
			else 
				pos = beginPostion[emptyIndex];
			return pos;
		}

		public void GetOnBoat(RolesController characterCtrl) {
			int index = getEmptyIndex ();
			passenger [index] = characterCtrl;
		}

		public RolesController GetOffBoat(string passenger_name) {
			for (int i = 0; i < passenger.Length; i++)
				if (passenger [i] != null && passenger [i].getName () == passenger_name) {
					RolesController charactorCtrl = passenger [i];
					passenger [i] = null;
					return charactorCtrl;
				}
			
			Debug.Log ("Cant find passenger in boat: " + passenger_name);
			return null;
		}

		public GameObject getGameobj() {
			return boat;
		}

		public int where() { // end->-1; begin->1
			return postion;
		}

		public int[] getRoleNum() {
			int[] count = {0, 0};
			for (int i = 0; i < passenger.Length; i++) {
				if (passenger [i] == null)
					continue;
				if (passenger [i].getType () == 0)	// 0->priest, 1->devil
					count[0]++;
				else
					count[1]++;
			}
			return count;
		}

		public void reset() {
			moveableScript.reset ();
			if (postion == -1)
				Move ();
			passenger = new RolesController[2];
		}
	}
}

ClickGUI

相应玩家的点击,将该动作传给相应类实现操作

public class ClickGUI : MonoBehaviour {
	UserAction action;
	RolesController characterController;

	public void setController(RolesController rolectrl) {
		characterController = rolectrl;
	}

	void Start() {
		action = Director.getInstance ().currentSceneController as UserAction;
	}

	void OnMouseDown() {
		if (gameObject.name == "boat") {
			action.moveBoat ();
		} else {
			action.characterIsClicked (characterController);
		}
	}
}

运行效果
源码

运行游戏的方法1:
下载asset替换自己新建的项目,并将两个GUI脚本挂载到空对象中,将firstconctroller脚本挂载到摄影机上。

运行游戏的方法2:
直接打开asset中的priestAndDevil.unity。

3、思考题【选做】

使用向量与变换,实现并扩展 Tranform 提供的方法,如 Rotate、RotateAround 等

对于i、j、k本身的几何意义可以理解为一种旋转,其中i旋转代表X轴与Y轴相交平面中X轴正向向Y轴正向的旋转,j旋转代表Z轴与X轴相交平面中Z轴正向向X轴正向的旋转,k旋转代表Y轴与Z轴相交平面中Y轴正向向Z轴正向的旋转,见下图:
四元数的旋转
静态方法 Quaternion.Euler(x,y,z),直观上按 z、x、y 轴旋转的一个序列。因为旋转矩阵连乘得到复合旋转,故 q = qy * qx * qz
Rotation

public void Rotate(Transform trans,float anglex, float angley, float anglez){
    Quaternion p1 = Quaternion.AngleAxis(anglez, Vector3.forward);
    Quaternion p2 = Quaternion.AngleAxis(anglex, Vector3.right);
    Quaternion p3 = Quaternion.AngleAxis(angley, Vector3.up);
    trans.rotation *= p3 * p2 * p1;
}

RotateAround
围绕通过世界坐标中点的轴旋转变换角度

public void RotateAround(Transform trans,Vector3 point, Vector3 axis, float angle){
   	Quaternion p = Quaternion.AngleAxis(angle,axis);
	Vector3 distance = myTransform.position - point;
  	trans.position = p * myTransform.position;
  	trans.rotation *= p;  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值