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

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

游戏对象的运动过程本质上就是游戏对象的空间位置(Position)、旋转角度(Rotation)、大小(Scale)三个属性随着时间在做某种特定的变化。

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

方法1:

复合运动,先写一个向右运动的脚本,再写一个向下运动的脚本,最后将两个脚本同时挂载到同一个物体上。

向右运动:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MoveRight : MonoBehaviour
{public int speed = 5;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){this.transform.position += speed * Vector3.right * Time.deltaTime;}
}

向下运动:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MoveDown : MonoBehaviour
{public float speed = 2;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){this.transform.position += speed * Vector3.down * Time.deltaTime;speed += 0.1f;}
}

方法2:

直接运用Vector3来改变position,将两个脚本合并为一个。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Move : MonoBehaviour
{public int speedx = 5;public float speedy = 2;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){transform.position += new Vector3(speedx * Time.deltaTime, -1 * speedy * Time.deltaTime, 0);speedy += 0.1f;}
}

方法3:

使用transform.Translate,将方法2中得出来变化向量传进Translate函数里面。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Move : MonoBehaviour
{public int speedx = 5;public float speedy = 2;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){transform.Translate(new Vector3(speedx * Time.deltaTime, -1 * speedy * Time.deltaTime, 0));speedy += 0.1f;}
}

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

首先到红动网下载太阳系的贴图素材。把太阳系各行星摆放好。

参照练习03-09,首先完成一个在同一法平面上的太阳系。

要想各行星不在一个法平面上,可以引入一个随机数,随机生成一个法向量,这样就避免了为每一个行星各自设计一个脚本。不过,由于地球还有一个卫星(月球),需要专门设计脚本,因此,总的来说,只需写两个脚本即可。

地球外的行星环绕脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Round : MonoBehaviour
{public Transform center;public int y,z,speed;// Start is called before the first frame updatevoid Start(){y = Random.Range(-10, 10);z = Random.Range(-10, 10);speed = Random.Range(10, 100);}// Update is called once per framevoid Update(){transform.RotateAround(center.transform.position, new Vector3(0, y, z), speed * Time.deltaTime);}
}

地球的环绕脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RoundOfEarth : MonoBehaviour
{public Transform sun;public Transform moon;public int y,z,speed;// Start is called before the first frame updatevoid Start(){y = Random.Range(-10, 10);z = Random.Range(-10, 10);speed = Random.Range(10, 100);}// Update is called once per framevoid Update(){this.transform.RotateAround(sun.position, new Vector3(0, y, z), speed * Time.deltaTime);moon.transform.RotateAround(this.transform.position, new Vector3(0, y, z), 20 * speed * Time.deltaTime);}
}

最终把各自的脚本挂载到每一颗行星上面。

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

游戏很简单,先送一个魔鬼上对岸(牧师和魔鬼过去,牧师回来或者两个魔鬼过去一个魔鬼回来);再送第二个魔鬼过去(此时需要两个魔鬼过去);然后两个牧师过河,牧师带一个魔鬼回去;两个牧师过对岸,最终让魔鬼把剩下的两个魔鬼运过去。

列出游戏中提及的事物(Objects)

牧师,恶魔,船,河流,两岸

用表格列出玩家动作表(规则表),注意,动作越少越好

当前状态 玩家操作 结果
船上有空位 点击靠近船一侧岸上的恶魔或牧师 对应的恶魔或牧师上船
牧师或恶魔在船上 点击船上的恶魔或牧师 对应的恶魔或牧师上靠近船的岸
其中任意一侧的恶魔的数量大于牧师的数量 游戏失败
恶魔和牧师全部到河边另一侧 游戏胜利

请将游戏中对象做成预制

下载场景

下载人物模型

将游戏对象做成预制:

开始编程

首先,下载好MVC结构程序的模板。代码里面就实现了简单的太阳系。

SSDirector和ISceneController都能够直接使用模板的,但IUserAction里面还需要新增人物以及船的移动接口,同时还多加了一个游戏状态检查接口,即:

随即对UserGUI里面的OnGUI函数稍作修改,新增打印游戏成功或失败的信息。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class UserGUI : MonoBehaviour {private IUserAction action;public string gameMessage;void Start () {action = SSDirector.getInstance ().currentSceneController as IUserAction;}void OnGUI() {  float width = Screen.width / 6;  float height = Screen.height / 12;action.Check();GUIStyle style = new GUIStyle();style.normal.textColor = Color.red;style.fontSize = 30;GUI.Label(new Rect(320,100,50,200),gameMessage,style);if (GUI.Button(new Rect(0, 0, width, height), "Restart")) {  action.Restart();  } string paused_title = SSDirector.getInstance ().Paused ? "Resume" : "Pause!"; if (GUI.Button(new Rect(width, 0, width, height), paused_title)) { SSDirector.getInstance ().Paused = !SSDirector.getInstance ().Paused;} }
}

然后就轮到最重要的部分,也就是FirstController的编写了,首先需要的是各种控制器以及当前游戏的各个状态变量:

 private LandModelController landRoleController;private BoatController boatRoleController;private RoleModelController[] roleModelControllers;private MoveController moveController;private bool isRuning;private int leftPriestNum;private int leftDevilNum;private int rightPriestNum;private int rightDevilNum;

需要修改原来的LoadResources函数,在这里只需要创建相应的Controller即可,具体的预制实例化交给相应的Controller来干。

 public void LoadResources () {landRoleController=new LandModelController();landRoleController.CreateLand();roleModelControllers=new RoleModelController[6];for(int i=0;i<6;i++){roleModelControllers[i]=new RoleModelController();roleModelControllers[i].CreateRole(i<3? true:false,i);roleModelControllers[i].GetRoleModel().role.transform.localPosition=landRoleController.AddRole(roleModelControllers[i].GetRoleModel());}boatRoleController=new BoatController();boatRoleController.CreateBoat();moveController=new MoveController();leftPriestNum=leftDevilNum=3;rightPriestNum=rightDevilNum=0;isRuning=true;}

同时还需要对IUserAction里定义的四个接口做实现,这些函数的实现都建立在游戏规则的基础上,而且同样地也只需调用相应的Controller即可。

 #region IUserAction implementationpublic void MoveBoat(){if(!isRuning||moveController.GetIsMoving()) return;if(boatRoleController.GetBoatModel().isRight){moveController.SetMove(new Vector3(3,-0.3f,-30),boatRoleController.GetBoatModel().boat);leftPriestNum+=boatRoleController.GetBoatModel().priestNum;leftDevilNum+=boatRoleController.GetBoatModel().devilNum;rightPriestNum-=boatRoleController.GetBoatModel().priestNum;rightDevilNum-=boatRoleController.GetBoatModel().devilNum;}else{moveController.SetMove(new Vector3(7.5f,-0.3f,-30),boatRoleController.GetBoatModel().boat);leftPriestNum-=boatRoleController.GetBoatModel().priestNum;leftDevilNum-=boatRoleController.GetBoatModel().devilNum;rightPriestNum+=boatRoleController.GetBoatModel().priestNum;rightDevilNum+=boatRoleController.GetBoatModel().devilNum;}boatRoleController.GetBoatModel().isRight=!boatRoleController.GetBoatModel().isRight;}public void MoveRole(RoleModel roleModel){if(!isRuning||moveController.GetIsMoving()) return;if(roleModel.isInBoat){roleModel.isRight=boatRoleController.GetBoatModel().isRight;moveController.SetMove(landRoleController.AddRole(roleModel),roleModel.role);boatRoleController.RemoveRole(roleModel);}else if(boatRoleController.GetBoatModel().isRight==roleModel.isRight){landRoleController.RemoveRole(roleModel);moveController.SetMove(boatRoleController.AddRole(roleModel),roleModel.role);}}public void Restart (){landRoleController.CreateLand();for(int i=0;i<6;i++){roleModelControllers[i].CreateRole(i<3? true:false,i);roleModelControllers[i].GetRoleModel().role.transform.localPosition=landRoleController.AddRole(roleModelControllers[i].GetRoleModel());}boatRoleController.CreateBoat();leftPriestNum=leftDevilNum=3;rightPriestNum=rightDevilNum=0;isRuning=true;this.gameObject.GetComponent<UserGUI>().gameMessage="";}public void Check(){if(!isRuning) return;this.gameObject.GetComponent<UserGUI>().gameMessage="";if(rightPriestNum==3&&rightDevilNum==3){this.gameObject.GetComponent<UserGUI>().gameMessage="You Win!!";isRuning=false;}else if((leftPriestNum!=0&&leftPriestNum<leftDevilNum)||(rightPriestNum!=0&&rightPriestNum<rightDevilNum)){this.gameObject.GetComponent<UserGUI>().gameMessage="Game Over!!";isRuning=false;}}#endregion

实现好FirstController之后,这么一来,游戏的大抵框架也已经做好了,接下来只需实现各个Controller以及相应的点击事件即可。

需要添加点击事件的就是人物模型跟船,两者对这事件的处理有些许不同,因而在此编写一个统一接口ClickAction

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface ClickAction
{void DealClick();
}

因而,在一个点击事件发生之后,就调用这个接口函数即可。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Click : MonoBehaviour
{ClickAction clickAction;public void setClickAction(ClickAction clickAction){this.clickAction=clickAction;}void OnMouseDown(){clickAction.DealClick();}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}

Move的实现完完全全就是这一节课的内容了(空间与运动),在实现了平移运动的基础上,新增一个MoveController来管理当前运动的对象,因为“船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!”

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Move : MonoBehaviour
{public bool isMoving=false;public float speed=3;public Vector3 des;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){if(transform.localPosition==des){isMoving=false;return;}isMoving=true;transform.localPosition=Vector3.MoveTowards(transform.localPosition,des,speed*Time.deltaTime);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MoveController
{private GameObject moveObject;public bool GetIsMoving(){return moveObject!=null&&moveObject.GetComponent<Move>().isMoving;}public void SetMove(Vector3 des,GameObject moveObject){Move test;this.moveObject=moveObject;if(!moveObject.TryGetComponent<Move>(out test)) moveObject.AddComponent<Move>();this.moveObject.GetComponent<Move>().des=des;}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}

至于船/人物/地面的实现,都是大同小异的,以小船为例,除了要实现上述的点击事件处理接口和创建小船以外,还需要对人物的上船下船进行管理,至于船的模型及相应的状态,又交给下一层BoatModel来完成。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BoatController : ClickAction
{BoatModel boatModel;IUserAction userAction;public BoatController(){userAction=SSDirector.getInstance().currentSceneController as IUserAction;}public void CreateBoat(){if(boatModel!=null) Object.DestroyImmediate(boatModel.boat);boatModel=new BoatModel();boatModel.boat.GetComponent<Click>().setClickAction(this);}public BoatModel GetBoatModel(){return boatModel;}public Vector3 AddRole(RoleModel roleModel){if(boatModel.roles[0]==null){boatModel.roles[0]=roleModel;roleModel.isInBoat=true;roleModel.role.transform.parent=boatModel.boat.transform;if(roleModel.isPriest) boatModel.priestNum++;else boatModel.devilNum++;return new Vector3(-0.2f,0.2f,0.5f);}if(boatModel.roles[1]==null){boatModel.roles[1]=roleModel;roleModel.isInBoat=true;roleModel.role.transform.parent=boatModel.boat.transform;if(roleModel.isPriest) boatModel.priestNum++;else boatModel.devilNum++;return new Vector3(-0.2f,0.2f,-0.6f);}return roleModel.role.transform.localPosition;}public void RemoveRole(RoleModel roleModel){roleModel.role.transform.parent=null;if(boatModel.roles[0]==roleModel){boatModel.roles[0]=null;if(roleModel.isPriest) boatModel.priestNum--;else boatModel.devilNum--;}else if(boatModel.roles[1]==roleModel){boatModel.roles[1]=null;if(roleModel.isPriest) boatModel.priestNum--;else boatModel.devilNum--;}}public void DealClick(){if(boatModel.roles[0]!=null||boatModel.roles[1]!=null){userAction.MoveBoat();}}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}

BoatModel也只需要对船进行实例化和初始化就好。不过需要注意的是,游戏对象需要添加BoxCollider才能够触发点击事件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BoatModel
{public GameObject boat;public RoleModel[] roles;public bool isRight;public int priestNum,devilNum;public BoatModel(){priestNum=devilNum=0;roles=new RoleModel[2];boat=GameObject.Instantiate(Resources.Load("WoodBoat", typeof(GameObject))) as GameObject;boat.transform.position=new Vector3(3,-0.3f,-30);boat.AddComponent<BoxCollider>();boat.AddComponent<Click>();boat.GetComponent<BoxCollider>().size=new Vector3(1.5f,0.6f,2.5f);isRight=false;}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}

剩下的人物以及地面的实现也跟小船差不多,详情可看完整代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RoleModelController : ClickAction
{RoleModel roleModel;IUserAction userAction;public RoleModelController(){userAction=SSDirector.getInstance().currentSceneController as IUserAction;}public void CreateRole(bool isPriest,int tag){if(roleModel!=null) Object.DestroyImmediate(roleModel.role);roleModel=new RoleModel(isPriest,tag);roleModel.role.GetComponent<Click>().setClickAction(this);}public RoleModel GetRoleModel(){return roleModel;}public void DealClick(){userAction.MoveRole(roleModel);}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RoleModel// : MonoBehaviour
{public GameObject role;public bool isPriest;public int tag;public bool isRight;public bool isInBoat;public Vector3 rightPos;public Vector3 leftPos;public RoleModel(bool isPriest,int tag){this.isPriest=isPriest;this.tag=tag;isRight=false;isInBoat=false;rightPos=new Vector3(9,0,-33.3f+tag*1.1f);leftPos=new Vector3(1,0,-33.3f+tag*1.1f);role=GameObject.Instantiate(Resources.Load(isPriest?"Priests"+tag:"Devils", typeof(GameObject))) as GameObject;role.transform.position=leftPos;role.AddComponent<Click>();role.AddComponent<BoxCollider>();role.GetComponent<BoxCollider>().size=new Vector3(0.6f,3,0.6f);}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LandModelController
{private LandModel landModel;public void CreateLand(){if(landModel==null) landModel=new LandModel();}public LandModel GetLandModel(){return landModel;}public Vector3 AddRole(RoleModel roleModel){roleModel.role.transform.parent=this.landModel.land.transform;roleModel.isInBoat=false;if(roleModel.isRight) return roleModel.rightPos;else return roleModel.leftPos;}public void RemoveRole(RoleModel roleModel){}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LandModel
{public GameObject land;public LandModel(){land=GameObject.Instantiate(Resources.Load("Environment", typeof(GameObject))) as GameObject;land.transform.position=new Vector3(0,0,0);}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}

最后感谢师兄的博客Unity实现Priests and Deivls游戏

3、思考题【选做】

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

创建一个空对象挂载脚本。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class test : MonoBehaviour
{public Transform obj;void Rotate(Vector3 axis,float angle,Space relativeTo=Space.Self){var rot=Quaternion.AngleAxis(angle,axis);obj.rotation*=rot;}void RotateAround(Vector3 point,Vector3 axis,float angle){var rot=Quaternion.AngleAxis(angle,axis);obj.position=point+(obj.position-point)*rot;obj.rotation*=rot;}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){Rotate(Vector3.up,30*Time.deltaTime);}
}

按照网上的说法来看,RotateAround似乎是可以这么实现,但是obj.position那一行会报一个error CS0019: Operator '*' cannot be applied to operands of type 'Vector3' and 'Quaternion',而obj.rotation却没有这样的报错信息,这是不是说明obj.rotation并不是Vector3类型??

不过总的来说,虽然RotateAround遇到了一点问题,但是Rotate也还是可行的。

3D游戏(3)——空间与运动相关推荐

  1. Unity空间与运动(中山大学3D游戏作业3)

    Unity空间与运动(中山大学3D游戏作业3) 目录 Unity空间与运动(中山大学3D游戏作业3) 一.程序验证 物体运动的本质 三种方法实现抛物线运动 实现太阳系 二.牧师与恶魔游戏 代码仓库:h ...

  2. 3D游戏:三、空间与运动

    1.简答并用程序验证[建议做] 1.1游戏对象运动的本质是什么? 游戏对象运动本质上是游戏对象空间属性的改变,包括Position和Rotation的变换. 1.2请用三种方法以上方法,实现物体的抛物 ...

  3. Unity 3D游戏代码编程学习教程 Full Guide To Unity 3D C#: Learn To Code Making 3D Games

    Unity 3D游戏代码编程学习教程 Full Guide To Unity 3D & C#: Learn To Code Making 3D Games Full Guide To Unit ...

  4. Direct3D 开发之旅 3D 游戏基本概念的介绍2

    文接上篇,上节说到了3D的重要的几何知识. 这节首先我们补充一些其他重要的3D的几何知识. 通过所有的变换,将顶点从物体局部坐标系变换到视口坐标系系统.变换方法以下几种 1. 平移,旋转和缩放等变换操 ...

  5. 【转】热门3D游戏视觉效果名词简介

    from: http://job.17173.com/content/2009-08-19/20090819181147996,1.shtml  前言 一部又一部的游戏大作降临,每个游戏都声称要给玩家 ...

  6. Android 3D游戏开发技术详解与典型案例

    下载地址 <Android3D游戏开发技术详解与典型案例>主要以Android平台下3D游戏的开发为主题,并结合真实的案例向读者详细介绍了OpenGL ES的基础 知识及3D游戏程序开发的 ...

  7. 3d游戏编程(转帖)

    3d游戏编程(转帖) 我先声明,我不是编程高手,我还只是个初学者,但我觉得我所知道的对刚入门3D游戏编程的新手,应该能让他们少走弯路,我也很想用朝语来写,但是朝语的词 汇库很久没有更新过了,有些专业的 ...

  8. Pygame学习笔记 6 —— 3D游戏

        pygame是是上世纪的产品,虽然不适合最3D游戏,但我可以使用pygame来绘制简单的3D图形,就像在白纸上画立体图形一样. 主要内容: 视觉上的远近.3D空间.绘制一个空间图形 一.视觉上 ...

  9. 3D游戏设计作业(三)

    一.游戏对象运动的本质: 是游戏游戏对象跟随每一帧在空间上发生变化.空间变化包括transform中position(绝对位置或相对位置)与rotation(所处角度)的变化. 二.实现物体的抛物线运 ...

最新文章

  1. SAP MM Transportation of PR Release Strategy with Classification
  2. c++ gdi修改dpi_最新高血压标准修改,包括确诊标准和用药方案!你的药吃对了吗?...
  3. WCF 第九章 诊断 系列文章
  4. RunTime运行时在iOS中的应用之UITextField占位符placeholder
  5. iOS 版 Skype支持群组语音聊天
  6. python爬虫 -- 正则表达式 与 Re模块的介绍
  7. 目前最常用的计算机机箱类型为_常用的计算机设备
  8. 玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化
  9. SqlHelper操纵数据库工具类
  10. FreeBSD重新加载rc.conf
  11. centos7 dotnet command not found
  12. linux 配置redis密码
  13. 【2017CCPC哈尔滨赛区 HDU 6242】Geometry Problem【随机化】
  14. Telos 首份年报(中译版-下)
  15. 等保2.0四级安全要求
  16. 上周六香山游兄弟们的合影
  17. xp无法访问win7计算机,xp连接win7共享打印机无法连接
  18. android设置个性桌面,打造小清新手机 安卓桌面 美化全教程
  19. 赛博念经!自带RGB的电子木鱼,能敲出《般若心经》,网友:想买
  20. 【Leetcode刷题Python】55. 跳跃游戏

热门文章

  1. Linux下全平台聊天工具,程序员的全平台聊天软件:Rocket.Chat
  2. 了解运营的本质,内容运营,用户运营,活动运营,产品运营
  3. STM32项目设计:温湿度空气质量报警器, 分享源码、PCB
  4. ESP32-CAM高性价比温湿度监控系统
  5. shell脚本基础知识(入门)
  6. javaj经典程序编程50题
  7. pycharm只显示左侧project,不显示项目目录
  8. 与狗尾草一起探寻人机交互的更多可能性|白洞战报
  9. html简单仓库管理网页代码,仓库管理.html
  10. ps里面怎么插入流程图_photoshop cs6绘画带箭头简单流程图的操作教程