3Dgame

编程入门 行业动态 更新时间:2024-10-07 08:31:53

3<a href=https://www.elefans.com/category/jswz/34/1461077.html style=Dgame"/>

3Dgame

3D游戏 作业4

  • 基本操作演练
  • 编程实践
      • 牧师与魔鬼 动作分离版
        • SSDirector
        • Interface
        • FirstSceneActionManager
        • Main
        • Judge
        • InteractGUI
        • CoastController
        • BoatController
        • Character
  • 材料与渲染联系(可选)
      • Standard Shader 自然场景渲染器
      • 声音

自学资源:

结构类型
枚举类型
const关键字

基本操作演练

下载 Fantasy Skybox FREE, 构建自己的游戏场景;写一个简单的总结,总结游戏对象的使用。

天空是任何游戏都离不开的设定,因其要按照季节与时间产生变化,是表示时间的重要工具,而天空盒是一种材料。从 Unity5 开始官方便不再提供天空资源,我们需要使用 skybox 搜索,从 Asset Store 中获取天空,如:Skybox,Fantasy Skybox FREE 等。


首先,打开项目,在 Asset Store 中搜索到 Fantasy Skybox FREE,下载并导入 Assats 中。如下:


如图所示,Assets文件夹中有很多的纹理、素材、预设,方便我们进行使用和设计。

建立天空盒,创建一个Material,并将Shader的值改为Skybox/6 Sided,再把天空盒每个面的贴图都贴上去,就弄好了一个天空盒了。



然后构建地形,我直接使用其中 demo 场景中自带的 Terrain 进行操作:


该地形中的功能大概有:


我下载导入的这个 Fantasy Skybox FREE 并没有树的 Assets,因此我试着创建一些草丛的游戏对象。

Fantasy Skybox FREE 中有很多草丛(灌木)的 Assets ,我们可以创建对象并使用他们;也可以在地形中使用 Paint Details 来创建草丛,选定各个参数后就可以自由选择添加它们的位置。

在左下角增添了一片圆形的花丛:


也可以通过 Edit Details 等来改变草丛和添加草丛的方式:



其中 Brush Size 是添加区域的大小,Opacity 是添加区域内草丛的疏密,Target Strength 是草的大小(长度)。

经过一些调整,我们得到了一片样子不同的草丛:


像这样,我们可以在 Asset Store 中下载资源或自己创造,再通过这些工具创造出各种各样的游戏场景。

编程实践

牧师与魔鬼 动作分离版

设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。

游戏要求和玩家动作表(规则表)都与上次作业的基本相同。

和上次作业不同的是,这次的牧师与魔鬼要做到动作分离,因此我们需要把所有游戏对象的动作都抽取出来,通过动作管理器进行统一安排。

在上次作业中,每个游戏对象有自己单独的控制器,使各个动作的脚本是挂载在各自的游戏对象身上执行的,看上去井井有条但不利于管理;而使用动作分离器,就是将这些动作都安排在一个总的控制管理器中,从而统一安排动作的执行。

SSDirector

导演类,使用单例模式保证其实例仅有一个,负责在场景初始化时控制对应场景的场记。

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;}public int getFPS(){return Application.targetFrameRate;}public void setFPS(int fps){Application.targetFrameRate = fps;}
}
Interface

接口命名空间,提供场景控制、玩家动作控制的接口,只给用户界面提供API,降低耦合性,体现了良好的封装。

namespace Interfaces
{public interface ISceneController{void LoadResources();}public interface UserAction{void MoveBoat();void ObjectIsClicked(Character characterCtrl);void Restart();}public enum SSActionEventType : int { Started, Completed }public interface SSActionCallback{void SSActionCallback(SSAction source);}
}
FirstSceneActionManager

动作控制器,管理船和游戏角色的移动,体现了动作分离的思想,脚本不需要挂载到对象身上运行,这是与上次作业不同的地方。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Interfaces;
public class FirstSceneActionManager : SSActionManager, SSActionCallback
{public SSActionEventType Complete = SSActionEventType.Completed;public void BoatMove(BoatController Boat){Complete = SSActionEventType.Started;CCMoveToAction action = CCMoveToAction.getAction(Boat.GetDestination(), Boat.GetMoveSpeed());addAction(Boat.GetGameObject(), action, this);Boat.ChangeState();}public void CharacterMove(Character GameObject, Vector3 Destination){Complete = SSActionEventType.Started;Vector3 CurrentPos = GameObject.GetPosition();Vector3 MiddlePos = CurrentPos;if (Destination.y > CurrentPos.y){MiddlePos.y = Destination.y;}else{MiddlePos.x = Destination.x;}SSAction action1 = CCMoveToAction.getAction(MiddlePos, GameObject.GetMoveSpeed());SSAction action2 = CCMoveToAction.getAction(Destination, GameObject.GetMoveSpeed());SSAction seqAction = CCSequenceAction.getAction(1, 0, new List<SSAction> { action1, action2 });this.addAction(GameObject.GetGameobject(), seqAction, this);}public void SSActionCallback(SSAction source){Complete = SSActionEventType.Completed;}
}
Main

主类,负责实例化接口 ISceneController 和 UserAction,实现加载资源和响应用户操作;
当裁判类返回一个游戏结束的信息,则场记通知玩家交互类 InteractGUI 显示结束信息。

public class FirstController : MonoBehaviour, ISceneController, UserAction
{public InteractGUI UserGUI;public CoastController fromCoast;public CoastController toCoast;public BoatController boat;public Judge judge;	 				//裁判类private Character[] Character;private FirstSceneActionManager FSAmanager;void Awake(){SSDirector director = SSDirector.getInstance();director.currentScenceController = this;UserGUI = gameObject.AddComponent<InteractGUI>() as InteractGUI;Character = new Character[6];			//6个游戏角色LoadResources();}void Start(){FSAmanager = GetComponent<FirstSceneActionManager>();}public void LoadResources(){fromCoast = new CoastController("from");toCoast = new CoastController("to");boat = new BoatController();judge = new Judge(fromCoast, toCoast, boat);	//初始化裁判类GameObject river = Instantiate(Resources.Load("Prefabs/river", typeof(GameObject)), new Vector3(0, -7, 10), Quaternion.identity, null) as GameObject;river.name = "river";//加载牧师for (int i = 0; i < 3; i++){Character p = new Character("priest");p.setName("priest" + i);p.setPosition(fromCoast.getEmptyPosition());p.getOnCoast(fromCoast);fromCoast.getOnCoast(p);Character[i] = p;}//加载魔鬼for (int i = 0; i < 3; i++){Character d = new Character("devil");d.setName("devil" + i);d.setPosition(fromCoast.getEmptyPosition());d.getOnCoast(fromCoast);fromCoast.getOnCoast(d);Character[i + 3] = d;}}	//鼠标点击事件public void ObjectIsClicked(Character Objects){if (FSAmanager.Complete == SSActionEventType.Started) return;if (Objects.isOnBoat()){CoastController whichCoast;if (boat.get_State() == -1){ whichCoast = toCoast;}else{whichCoast = fromCoast;}//下船动作boat.GetOffBoat(Objects.getName());FSAmanager.CharacterMove(Objects, 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());FSAmanager.CharacterMove(Objects, boat.getEmptyPosition());Objects.getOnBoat(boat);boat.GetOnBoat(Objects);}//通知controller游戏结束,显示win或loseUserGUI.SetState = judge.Check();}//船移动的动作public void MoveBoat(){if (FSAmanager.Complete == SSActionEventType.Started || boat.isEmpty()) return;FSAmanager.BoatMove(boat);       UserGUI.SetState = judge.Check();		//新增的裁判类}//游戏结束,玩家重新开始的动作public void Restart(){fromCoast.reset();toCoast.reset();foreach (Character gameobject in Character){gameobject.reset();}boat.reset();}
}
Judge

裁判类,通过计数来判断游戏是否结束。

public class Judge : MonoBehaviour
{CoastController fromCoast;CoastController toCoast;public BoatController boat;public Judge(CoastController c1,CoastController c2, BoatController b){fromCoast = c1;toCoast = c2;boat = b;}public int Check(){   // 0->not finish, 1->lose, 2->winint 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)      // winreturn 2;int[] boatCount = boat.GetobjectsNumber();if (boat.get_State() == -1){   // boat at toCoastto_priest += boatCount[0];to_devil += boatCount[1];}else{   // boat at fromCoastfrom_priest += boatCount[0];from_devil += boatCount[1];}if (from_priest < from_devil && from_priest > 0){       // losereturn 1;}if (to_priest < to_devil && to_priest > 0){return 1;}return 0;           // not finish}
}
InteractGUI

UI交互界面设定,实现用户的点击事件,并在游戏结束后显示结果。

public class InteractGUI : MonoBehaviour
{UserAction UserAcotionController;public int SetState { get { return GameState; } set { GameState = value; } }static int GameState = 0;void Start(){UserAcotionController = SSDirector.getInstance().currentScenceController as UserAction;}private void OnGUI(){if (GameState == 1){GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2-50, 100, 50), "Gameover!");if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart")){GameState = 0;UserAcotionController.Restart();}}else if (GameState == 2){GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2-50, 100, 50), "You Win!");if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart")){GameState = 0;UserAcotionController.Restart();}}}
}
public class ClickGUI : MonoBehaviour
{UserAction UserAcotionController;Character GameObjectsInScene;public void setController(Character characterCtrl){GameObjectsInScene = characterCtrl;}void Start(){UserAcotionController = SSDirector.getInstance().currentScenceController as UserAction;}void OnMouseDown(){if (gameObject.name == "boat"){UserAcotionController.MoveBoat();}else{UserAcotionController.ObjectIsClicked(GameObjectsInScene);}}
}
CoastController

河岸控制器,返回河岸上空余的位置以便游戏角色落脚。

public class CoastController
{readonly GameObject coast;readonly Vector3 from_pos = new Vector3(-16, -6, 10);readonly Vector3 to_pos = new Vector3(16, -6, 10);readonly Vector3[] positions;readonly int State;    // to->-1, from->1Character[] passengerPlaner;public CoastController(string _State){positions = new Vector3[] {new Vector3(-21,-2.5F,10), new Vector3(-19,-2.5F,10), new Vector3(-17,-2.5F,10),new Vector3(-15,-2.5F,10), new Vector3(-13,-2.5F,10), new Vector3(-11,-2.5F,10)};passengerPlaner = new Character[6];if (_State == "from"){coast = Object.Instantiate(Resources.Load("Prefabs/land1", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;coast.name = "from";State = 1;}else{coast = Object.Instantiate(Resources.Load("Prefabs/land2", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;coast.name = "to";State = -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 *= State;return pos;}public void getOnCoast(Character ObjectControl){int index = getEmptyIndex();passengerPlaner[index] = ObjectControl;}public Character getOffCoast(string passenger_name){   // 0->priest, 1->devilfor (int i = 0; i < passengerPlaner.Length; i++){if (passengerPlaner[i] != null && passengerPlaner[i].getName() == passenger_name){Character charactorCtrl = passengerPlaner[i];passengerPlaner[i] = null;return charactorCtrl;}}return null;}public int get_State(){return State;}public int[] GetobjectsNumber(){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->devilcount[0]++;}else{count[1]++;}}return count;}public void reset(){passengerPlaner = new Character[6];}
}
BoatController

船的控制类,提供船上的空余位置,并协助实现角色上下船。

public class BoatController
{readonly GameObject boat;readonly Vector3 fromPosition = new Vector3(-8, -5, 10);readonly Vector3 toPosition = new Vector3(8, -5, 10);readonly Vector3[] from_positions;readonly Vector3[] to_positions;int State; // to->-1; from->1Character[] passenger = new Character[2];int Speed = 15;int MovingState = -1; // Move = 1;Not Move = -1;public BoatController(){State = 1;MovingState = -1;from_positions = new Vector3[] { new Vector3(-9, -3.5F, 10), new Vector3(-7, -3.5F, 10) };to_positions = new Vector3[] { new Vector3(7, -3.5F, 10), new Vector3(9, -3.5F, 10) };boat = Object.Instantiate(Resources.Load("Prefabs/boat", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;boat.name = "boat";boat.AddComponent(typeof(ClickGUI));}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 (State == -1){pos = to_positions[emptyIndex];}else{pos = from_positions[emptyIndex];}return pos;}public void GetOnBoat(Character ObjectControl){int index = getEmptyIndex();passenger[index] = ObjectControl;}public Character GetOffBoat(string passenger_name){for (int i = 0; i < passenger.Length; i++){if (passenger[i] != null && passenger[i].getName() == passenger_name){Character charactorCtrl = passenger[i];passenger[i] = null;return charactorCtrl;}}return null;}public GameObject GetGameObject(){return boat;}public void ChangeState(){State = -State;}public int get_State(){ // to->-1; from->1return State;}public int[] GetobjectsNumber(){int[] count = { 0, 0 };// [0]->priest, [1]->devilfor (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 Vector3 GetDestination(){if (State == 1) return toPosition;else return fromPosition;}public int GetMoveSpeed(){return Speed;}public void reset(){State = 1;boat.transform.position = fromPosition;passenger = new Character[2];MovingState = -1;}public int GetMovingState(){return MovingState;}public void ChangeMovingstate(){MovingState = -MovingState;}
}
Character

游戏角色的控制(下岸、上船、下船、上岸),记录位置信息(是否在岸上,是在左岸还是右岸)和移动信息(是否处于移动状态,是则无法响应点击事件)。

public class Character
{CoastController coastController;readonly GameObject Instance;readonly ClickGUI clickGUI;readonly int characterType; // 0->priest, 1->devilint MovingState = -1; // Move = 1;Not Move = -1;bool _isOnBoat = false;public Character(string Type){MovingState = -1;if (Type == "priest"){Instance = Object.Instantiate(Resources.Load("Prefabs/priests", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;characterType = 0;}else{Instance = Object.Instantiate(Resources.Load("Prefabs/devils", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;characterType = 1;}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 int getType(){   // 0->priest, 1->devilreturn characterType;}public string getName(){return Instance.name;}public void getOnBoat(BoatController boatCtrl){coastController = null;Instance.transform.parent = boatCtrl.GetGameObject().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 Vector3 GetPosition(){return Instance.transform.position;}public int GetMoveSpeed(){return 20;}public GameObject GetGameobject(){return Instance;}public void reset(){coastController = (SSDirector.getInstance().currentScenceController as FirstController).fromCoast;getOnCoast(coastController);setPosition(coastController.getEmptyPosition());coastController.getOnCoast(this);MovingState = -1;}public int GetMovingState(){return MovingState;}public void ChangeMovingstate(){MovingState = -MovingState;}
}

材料与渲染联系(可选)

Standard Shader 自然场景渲染器

阅读官方 Standard Shader 手册;
选择合适内容,如 Albedo Color and Transparency,寻找合适素材,用博客展示相关效果的呈现。

使用 Standard Shader 作为自然场景的渲染是一种物理渲染方式。

在一般的光照计算中,基本上是基于模拟的模型,即是尽可能的模拟我们看上去的物体反射的颜色,而基于物理的光照计算则是依据了光线传播的物理特性,使用一些近似计算,让材质更加贴近于真实情况,所以物理渲染在表现自然界的物体时会看上去更加真实。

这种渲染可以节能,保证物体反射的光线不会超过它们接受的光线;且材质的镜面反射越强,漫反射就越少,曲面越平滑,高光就会越强、越小;另外还具有高动态范围(HDR),即颜色不再局限于0-1的范围内了。

声音

阅读官方 Audio 手册;
用博客给出游戏中利用 Reverb Zones 呈现车辆穿过隧道的声效的案例。

Unity的音频特征包括全三维空间声音、实时混合和掌握、混合器的层次、快照、预定义效果等等。Reverb Zones即是混响区域,其中包括三个基本属性:

  • 最小距离:表示gizmo中内圈的半径,决定渐变混响和完全混响的区域;
  • 最大距离:表示gizmo中外圈的半径,决定没有效果和混响逐渐开始应用了的区域;
  • 混响预设:确定混响区域使用的混响效果。

在混响区中,声音的工作原理:


要呈现车辆穿过隧道的声效,可以在 Component -> Audio -> Audio Reverb Zones 中给一个物体添加混响区域,并将混响的属性调成Cave模式;


然后导入音频文件,并创建一个Audio Source,将音频文件和其进行挂载,就可以在原有声音的基础上模拟出车穿过隧道的效果了。

更多推荐

3Dgame

本文发布于:2024-02-07 10:47:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1756364.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Dgame

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!