3D游戏作业3

1.使用Unity实现完整的太阳系

要求:行星围绕太阳的转速必须不同,且不在一个法平面上。

过程:首先寻找太阳系各星球的贴图,并将其加载到项目的assets中。[行星贴图网址](Solar Textures | Solar System Scope)

  1. 创建几个Sphere并命名为太阳系中各个星球的名字,然后将各个贴图拖到对应到星球上,这样就形做成了各个星球的预制。

  1. 将各行星以太阳为中心拖到其在太阳系中对应的位置,并设置大小。

  1. 给太阳添加光源效果,即在太阳所在的位置添加一个Point Light起到光源效果,并将光的颜色调整为橙黄色。

  1. 此时太阳看起来有点暗,所以需要让太阳的表面有发光效果。

将太阳的贴图组件勾选Emission并将颜色设置为橙红。


  1. 使用代码使得行星围绕太阳,月亮围绕地球运转。

需要注意如果围绕一个自转的星球公转会受到该星球的自转影响,所以需要克隆一个太阳和地球并令这两个克隆星球不自转,并把其他星球公转的对象设置为这两个不自转的星球。

  1. 为了能够显示行星的运行轨迹,需要给每个行星都添加一个组件Trial Render,具体位置是Component->Effects->Trial Render。

添加后的运行效果图如下

可以看到行星在不同法平面运转。

代码如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Rotate : MonoBehaviour {public Transform mercury;public Transform venus;public Transform earth;public Transform mars;public Transform jupiter;public Transform saturn;public Transform uranus;public Transform neptune;public Transform moon;public Transform earthclone;public Transform sun;// Use this for initializationvoid Start () {mercury = this.transform.Find("Mercury");venus = this.transform.Find("Venus");earth = this.transform.Find("Earth");mars = this.transform.Find("Mars");jupiter = this.transform.Find("Jupiter");saturn = this.transform.Find("Saturn");uranus = this.transform.Find("Uranus");neptune = this.transform.Find("Neptune");earthclone = this.transform.Find("EarthClone");moon = earthclone.Find("Moon");sun = this.transform.Find("Sun");}// Update is called once per framevoid Update () {sun.Rotate(Vector3.up * Time.deltaTime * 30);mercury.RotateAround(this.transform.position, new Vector3(1, 5, 0), 24 * Time.deltaTime);mercury.Rotate(Vector3.up * Time.deltaTime * 300);venus.RotateAround(this.transform.position, new Vector3(2, 12, 0), 33 * Time.deltaTime);venus.Rotate(Vector3.up * Time.deltaTime * 280);earth.RotateAround(this.transform.position, new Vector3(1, 10, 0), 19 * Time.deltaTime);earth.Rotate(Vector3.up * Time.deltaTime * 250);mars.RotateAround(this.transform.position, new Vector3(2, 11, 0), 24 * Time.deltaTime);mars.Rotate(Vector3.up * Time.deltaTime * 220);jupiter.RotateAround(this.transform.position, new Vector3(1, 10, 0), 13 * Time.deltaTime);jupiter.Rotate(Vector3.up * Time.deltaTime * 180);saturn.RotateAround(this.transform.position, new Vector3(1, 11, 0), 10 * Time.deltaTime);saturn.Rotate(Vector3.up * Time.deltaTime * 160);uranus.RotateAround(this.transform.position, new Vector3(2, 7, 0), 18 * Time.deltaTime);uranus.Rotate(Vector3.up * Time.deltaTime * 150);neptune.RotateAround(this.transform.position, new Vector3(3, 10, 0), 22 * Time.deltaTime);neptune.Rotate(Vector3.up * Time.deltaTime * 140);earthclone.RotateAround(this.transform.position, new Vector3(1, 10, 0), 30 * Time.deltaTime);moon.transform.RotateAround (earthclone.transform.position, new Vector3(1, 12, 0), 50 * Time.deltaTime);}
}

牧师与魔鬼小游戏

游戏说明

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 !

  1. 游戏中提及的事物

牧师,魔鬼,船,河,两边河岸。

  1. 用表格列出玩家的动作表
动作 效果 条件
玩家点击岸上角色 角色上船 1.船上有位置 2.角色在同一个岸边 3.没有其他角色在移动
玩家点击船上角色 角色下船 1.船已经到岸边 2.没有其他角色在移动
玩家点击船 船移动到对岸 1.船上有任意角色 2.没有其他角色在移动
玩家点击重新开始 所有角色重置
  1. 将各个角色对象做成预制。

使用有牧师贴图的圆球表示牧师,使用有恶魔贴图的方块表示恶魔。

  1. 在场景控制器LoadResources方法中加载游戏中的对象

场景控制器

对应的LoadResources代码如下

public void LoadResources(){//由于有重新开始按钮,所以如果不是第一次加载得把之前的物体摧毁if(flag){for(int i = 0; i < 6; i++){if(roleCtrl_list[i] != null){Destroy(roleCtrl_list[i].GetModelGameObject());}}for(int i = 0; i < 2; i++){if(landCtrl_list[i] != null){Destroy(landCtrl_list[i].GetModelGameObject());}}if(BoatCtrl != null){Destroy(BoatCtrl.GetModelGameObject());}}// 加载控制器和模型BoatCtrl = new BoatController();BoatCtrl.CreateModel();water1=new Water(new Vector3(0,-3.5f,12));for(int i = 0; i < 6; i++){int roleType = (i < 3) ? PRIEST : DEVIL;RoleController tmp=new RoleController(roleType, rolesID[i]);roleCtrl_list.Add(tmp);roleCtrl_list[i].CreateModel();}LandController tmp1 = new LandController(LEFTLAND, rolesID);landCtrl_list.Add(tmp1);LandController tmp2 = new LandController(RIGHTLAND, rolesID);landCtrl_list.Add(tmp2);landCtrl_list[0].CreateModel();landCtrl_list[1].CreateModel();MoveCtrl = new MoveController();flag=true;flag1=false;//开始游戏gameState = PLAYING;}//将角色的ID转换成数组的下标int IDToNumber(int ID){for(int i = 0; i < 6; i++){if(rolesID[i] == ID){return i;}}return -1;}//点击船时执行public void MoveBoat(){if(BoatCtrl.getEmptySeat()==0){flag1=true;}else{if(gameState != PLAYING || MoveCtrl.IsMoving()) return;CheckAndSetGameState();if(BoatCtrl.onLeftside){MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatRightPos);for(int i = 0; i < 2; i++){if(BoatCtrl.seat[i] != -1){RoleController r = roleCtrl_list[IDToNumber(BoatCtrl.seat[i])];MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatRightPos[i]);}}}else{MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatLeftPos);for(int i = 0; i < 2; i++){if(BoatCtrl.seat[i] != -1){RoleController r = roleCtrl_list[IDToNumber(BoatCtrl.seat[i])];MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatLeftPos[i]);}} }BoatCtrl.onLeftside = !BoatCtrl.onLeftside;}}
  1. 使用List集合类型组织对象

    比如下面用list组织角色控制类和岸边控制类。

  1. 由代码生成对象

    游戏开始前

    游戏开始后

  1. 介绍本游戏所使用的MVC框架

    7.1 Models模型

    ​ 7.1.1 Click脚本

    ​ Click类主要是为其他需要被点击的物体类提供一个功能组件。

    public class Click : MonoBehaviour
    {IObjectController clickAction;public void setClickAction(IObjectController clickAction) {this.clickAction = clickAction;}void OnMouseDown() {clickAction.DealClick();}
    }
    

    ​ 7.1.2 Postion类

    ​ Position类的成员变量用于存储其他物体的位置,要修改其他物体的位置只需修改Positon中的成员变量。

    ​ 7.1.3 其他类似land和role之类的对象类。

    ​ 这些类主要是加载预制中的物体,如果该物体需要被点击,就添加上述的Click组件,点击后发生的事件会在后续的control中详细定义。这里以Boat类为例。

    public class Boat
    {public GameObject boat;//船对象public Boat(Vector3 initPos){boat = GameObject.Instantiate(Resources.Load("Prefabs/boat", typeof(GameObject))) as GameObject;boat.transform.position = initPos;boat.AddComponent<Click>();}
    }
    

    7.2 Controller控制器

    ​ 7.2.1 Director对象

    ​ 这里采用单实例模式,Director类主要用于获取当前游戏场景,管理游戏全局状态等。第一次调用时会新建一个Director对象,而后面调用的时候不会创建新的对象,而是返回第一次调用时创建的对象,所以全局都 只会有一个Director,方便全局管理。

public class SSDirector : System.Object
{static SSDirector _instance;public ISceneController CurrentSceneController {get; set;}public static SSDirector GetInstance() {if (_instance == null) {_instance = new SSDirector();}return _instance;}
}

​ 7.2.2 IUserAction

​ IUserAction是用户对象接口,用于接收玩家的动作,分离了玩家的操作和游戏内部的逻辑,方便管理。

public interface IUserAction {void MoveBoat();void MoveRole(int id);int GetGameState();void Restart();
}

​ 7.2.3 ISceneController

​ ISceneController是场景控制接口,这个接口分离了导演和场景的控制,导演不需要知道如何实现场景控制,只需要让场景控制类去执行某项任务。

public interface ISceneController
{void Initialize();void LoadResources();
}

​ 7.2.4 各种类似BoatController,LandController之类的物体控制类

​ 这些物体控制类需要定义各个物体具有的属性以及使用函数实现各个物体需要完成的动作,下面以BoatController为例,其他物体控制类在代码中可以查阅。

​ BoatController类需要有的属性:

​ 1.bool类型的变量onLeftside, 用于判断船是否位于左岸。

​ 2.长度为2的List类型的数组seat,用于记录船上的两个位置的角色的编号。

​ 3.boat类对象boatmodel,加载了预制的物体。

​ BoatController类需要完成的动作:

1.Reset() 重置船上的座位。

2.embark(int roleID) 将编号为roleID的角色上船。

​ 3.getEmptySeat() 获取空座位的下标,如果没有则返回-1。

​ 4.disembark(int roleID) 将编号为roleID的角色下船。

​ 5.DealClick() 处理点击事件。

​ 6.GetModelGameObject() 返回boat对象。

7.CreateModel() 创建模型。

​ 7.2.5 FirstController

​ FirstController是最核心的控制器,主要任务是管理所有游戏对象,响应外部事件,设计游戏内部逻辑等,同时定义了各种事件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FirstController : MonoBehaviour, ISceneController, IUserAction
{public static int LEFTLAND = 0;public static int RIGHTLAND = 1;public static int BOAT = 2;public static bool flag = false;public static bool flag1=false;public static int PRIEST = 0;public static int DEVIL = 1;public static int PLAYING = 0;public static int WIN = 1;public static int FAILED = 2;Water water1;BoatController BoatCtrl;public List<RoleController> roleCtrl_list=new List<RoleController>();public List<LandController> landCtrl_list=new List<LandController>();MoveController MoveCtrl;int[] rolesID = new int[6]{0,1,2,3,4,5};int gameState;void Awake(){SSDirector director = SSDirector.GetInstance();director.CurrentSceneController = this;director.CurrentSceneController.LoadResources();}public void Initialize(){LoadResources();}public void LoadResources(){//由于有重新开始按钮,所以如果不是第一次加载得把之前的物体摧毁if(flag){for(int i = 0; i < 6; i++){if(roleCtrl_list[i] != null){Destroy(roleCtrl_list[i].GetModelGameObject());}}for(int i = 0; i < 2; i++){if(landCtrl_list[i] != null){Destroy(landCtrl_list[i].GetModelGameObject());}}if(BoatCtrl != null){Destroy(BoatCtrl.GetModelGameObject());}}// 加载控制器和模型BoatCtrl = new BoatController();BoatCtrl.CreateModel();water1=new Water(new Vector3(0,-3.5f,12));for(int i = 0; i < 6; i++){int roleType = (i < 3) ? PRIEST : DEVIL;RoleController tmp=new RoleController(roleType, rolesID[i]);roleCtrl_list.Add(tmp);roleCtrl_list[i].CreateModel();}LandController tmp1 = new LandController(LEFTLAND, rolesID);landCtrl_list.Add(tmp1);LandController tmp2 = new LandController(RIGHTLAND, rolesID);landCtrl_list.Add(tmp2);landCtrl_list[0].CreateModel();landCtrl_list[1].CreateModel();MoveCtrl = new MoveController();flag=true;flag1=false;//开始游戏gameState = PLAYING;}//将角色的ID转换成数组的下标int IDToNumber(int ID){for(int i = 0; i < 6; i++){if(rolesID[i] == ID){return i;}}return -1;}//点击船时执行public void MoveBoat(){if(BoatCtrl.getEmptySeat()==0){flag1=true;}else{if(gameState != PLAYING || MoveCtrl.IsMoving()) return;CheckAndSetGameState();if(BoatCtrl.onLeftside){MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatRightPos);for(int i = 0; i < 2; i++){if(BoatCtrl.seat[i] != -1){RoleController r = roleCtrl_list[IDToNumber(BoatCtrl.seat[i])];MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatRightPos[i]);}}}else{MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatLeftPos);for(int i = 0; i < 2; i++){if(BoatCtrl.seat[i] != -1){RoleController r = roleCtrl_list[IDToNumber(BoatCtrl.seat[i])];MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatLeftPos[i]);}} }BoatCtrl.onLeftside = !BoatCtrl.onLeftside;}}//角色被点击public void MoveRole(int id){flag1=false;int num = IDToNumber(id);if(gameState != PLAYING || MoveCtrl.IsMoving()) return;int seat;switch(roleCtrl_list[num].roleState){//角色在左岸的情况case 0: if(!BoatCtrl.onLeftside) return;landCtrl_list[0].LeaveLand(id);seat = BoatCtrl.embark(id);roleCtrl_list[num].GoTo(BOAT);if(seat == -1) return;MoveCtrl.SetMove(roleCtrl_list[num].GetModelGameObject(), Position.seatLeftPos[seat]);break;//角色在右岸的情况case 1: if(BoatCtrl.onLeftside) return;landCtrl_list[1].LeaveLand(id);seat = BoatCtrl.embark(id);roleCtrl_list[num].GoTo(BOAT);if(seat == -1) return;MoveCtrl.SetMove(roleCtrl_list[num].GetModelGameObject(), Position.seatRightPos[seat]);break;//角色在船上的情况case 2:if(BoatCtrl.onLeftside){seat = landCtrl_list[0].getEmptySeat();BoatCtrl.disembark(id);landCtrl_list[0].GoOnLand(id);roleCtrl_list[num].GoTo(LEFTLAND);MoveCtrl.SetMove(roleCtrl_list[num].GetModelGameObject(), Position.roleLeftPos[seat]);}else{seat = landCtrl_list[1].getEmptySeat();BoatCtrl.disembark(id);landCtrl_list[1].GoOnLand(id);roleCtrl_list[num].GoTo(RIGHTLAND);MoveCtrl.SetMove(roleCtrl_list[num].GetModelGameObject(), Position.roleRightPos[seat]);}break;default: break;}}//判断游戏状态public void CheckAndSetGameState(){if(gameState != PLAYING) return;//失败的情况int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};foreach(RoleController r in roleCtrl_list){rolePos[r.roleType, r.roleState]++;}if((rolePos[0,0]>0 && rolePos[0,0]<rolePos[1,0]) ||  (rolePos[0,1]>0 && rolePos[0,1]<rolePos[1,1]) ||(rolePos[0,2]>0 && rolePos[0,2] < rolePos[1,2])){gameState = FAILED;return;}  //成功的情况foreach(RoleController r in roleCtrl_list){if(r.roleType == 0 && r.roleState != RIGHTLAND){return;}}gameState = WIN;return;}//重置public void Restart(){LoadResources();gameState = PLAYING;}//获取游戏当前状态public int GetGameState(){return gameState;}
}

​ 7.3 View视图

​ UserGUI是用户程序界面,用于呈现给用户游戏画面以及各种按钮。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class UserGUI : MonoBehaviour
{IUserAction userAction;GUIStyle msgStyle, titleStyle;void Start(){userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;msgStyle = new GUIStyle();msgStyle.normal.textColor = Color.red;msgStyle.alignment = TextAnchor.MiddleCenter;msgStyle.fontSize = 30;titleStyle = new GUIStyle();titleStyle.normal.textColor = Color.black;titleStyle.alignment = TextAnchor.MiddleCenter;titleStyle.fontSize = 40;}// Update is called once per framevoid OnGUI() {// 重新开始的按钮if(GUI.Button(new Rect(Screen.width*0.4f, Screen.height*0.85f, Screen.width*0.2f, Screen.height*0.1f), "重新开始")){userAction.Restart();}// 检查是否正确GUI.Label(new Rect(0, 0, Screen.width, Screen.height*0.2f), "牧师与恶魔", titleStyle);if(userAction.GetGameState() == FirstController.WIN){GUI.Label(new Rect(0, Screen.height*0.2f, Screen.width, Screen.height*0.2f), "成功", msgStyle);}     else if(userAction.GetGameState() == FirstController.FAILED){GUI.Label(new Rect(0, Screen.height*0.2f, Screen.width, Screen.height*0.2f), "失败", msgStyle);}if(FirstController.flag1==true){GUI.Label(new Rect(0, Screen.height*0.2f, Screen.width, Screen.height*0.2f), "船为空,不能行动", msgStyle);}}
}

游戏画面


代码地址 我的仓库

简易太阳系及牧师与魔鬼相关推荐

  1. 空间与运动——简单太阳系+牧师与魔鬼

    作业内容 一.简答并用程序验证 1.游戏对象运动的本质是什么? 2.请用三种方法以上方法,实现物体的抛物线运动 3.简单太阳系 二.编程实践 1.脚本设计 2.效果图 一.简答并用程序验证 1.游戏对 ...

  2. unity实现牧师与魔鬼问题回答

    unity实现牧师与魔鬼&问题回答 简答题 游戏对象运动的本质是什么? 游戏运动本质就是使用矩阵变换(平移.旋转.缩放)改变游戏对象的空间属性. 请用三种方法以上方法,实现物体的抛物线运动.( ...

  3. 空间与运动——模拟太阳系、牧师与魔鬼游戏实现详解

    文章目录 一.简答并用程序验证: 1.1 游戏对象运动的本质是什么? 1.2 请用三种方法以上方法,实现物体的抛物线运动 1.3 写一个程序,实现一个完整的太阳系, 其他星球围绕太阳的转速必须不一样, ...

  4. Unity——牧师与魔鬼

    简答并用程序验证 游戏对象运动的本质是什么? 游戏对象运动的本质,就是游戏对象跟随每一帧发生变化的过程.在变化的过程中,主要是指游戏对象的transform中的rotation与position两个属 ...

  5. 牧师与魔鬼——动作分离版

    牧师与魔鬼--动作分离版 在上周的作业中,牧师与魔鬼游戏中的各个事件,都是写在Director中,并且都是继承Monobehavior的.在这周动作分离的设计中,我将上船.下船以及船的移动都分离出来. ...

  6. Unity实战之牧师与魔鬼(动作分离版)

    Unity实战之牧师与魔鬼(动作分离版) 项目链接 整体描述 本次项目在第一版牧师与魔鬼的基础上,将动作从场记中分离出来,并设计一个裁判类实时监测游戏进行的情况.这样改进的优点有很多: 降低了不同功能 ...

  7. 牧师与魔鬼 -- version2 动作分离

    目录 一.基本操作演练 1.下载 Fantasy Skybox FREE, 构建自己的游戏场景 2.写一个简单的总结,总结游戏对象的使用 二.编程实践 1.牧师与魔鬼 动作分离版 面向对象的游戏编程 ...

  8. Unity牧师与魔鬼小游戏(动作分离版)

    Unity牧师与魔鬼小游戏(动作分离版) 前言 这是中大计算机学院3D游戏编程课的一次作业,在这里分享一下设计思路. 主要代码上传到了gitee上,请按照后文的操作运行. 项目地址:https://g ...

  9. Unity3D学习—牧师与魔鬼—MVC模式和ECS架构应用

    需求 Priests and Devils Priests and Devils is a puzzle game in which you will help the Priests and Dev ...

最新文章

  1. Cordova入门系列(一)创建项目
  2. 赞!《Python面试大全》PDF版来啦!
  3. [初级]Java命令学习系列(七)——javap
  4. 一 MVC - HtmlHelper
  5. singer页左侧滚动的时候右侧跟随高亮显示
  6. 普联技术java工程师_【普联技术(TP-LINK)工资】java开发工程师待遇-看准网
  7. 蒙特卡洛粒子滤波定位算法_基于粒子滤波的TBD算法仿真—MATLAB仿真
  8. Gartner报告:2010年全球安全软件市场增长11%
  9. php实现先序、中序、后序遍历二叉树
  10. Android 蓝牙驱动专题分析(2)--- 蓝牙驱动代码流程、kernel dump、tombstone问题分析
  11. (转载)神级代码编辑器 Sublime Text 全程指南
  12. JavaScript速成
  13. Linux操作系统安全加固总结
  14. drwxr-xr-x是啥意思
  15. python实现GUI设计的方法
  16. 支付宝个人支付接口,无需营业执照
  17. WEB电商项目广告管理与缓存解决方案
  18. dependency一直报错,换各种jar包都不行
  19. Angular6笔记(4)
  20. Python编程经典案例【考题】自由落体运动球的运动轨迹

热门文章

  1. Orange学习-1(Orange启动的两种方法,附有安装包)
  2. 实验一计算机基础和网络知识竞赛,1、计算机知识竞赛活动方案
  3. 谷歌浏览器控制台修改源代码并保存修改到文件
  4. 安卓Android手机系统内文件夹全解
  5. Mall4j电商小程序源码分享
  6. 第四章 前馈神经网络
  7. IAR FOR 430出现Failed to re-intialize一种可能的解决方案
  8. windows编译opencv opencv-contrib
  9. python体测成绩数据分析_【Python数据分析】四级成绩分布 -matplotlib,xlrd 应用
  10. houdini volumetrail