Unity教程学习

教程1.Roll-a-ball

需求说明:通过键盘输入控制,上下左右等按钮,控制小球移动。移动小球收集物体,统计分数。

1).创建游戏

1.1 新建项目

打开unity页面,选择NEW,然后输入项目名,选择保存的位置,选择3D模式。
Unity教程学习_第1张图片

1.2 新建场景

左上方菜单栏File–New Scene,点击后,会再Hierarchy窗口看到一个场景,如下。
Unity教程学习_第2张图片
在上图蓝色选中条的右边的设置按钮,点击选择‘Save Scene As’,然后选择保存的位置并输入场景名称。保存完成后可在Project窗口中看到。
Unity教程学习_第3张图片

1.3 新建地面与配置Material

菜单GameObject–3D Object–Plane,将会在场景里创建一个只有一个面的平面游戏对象,并修改名称(window平台F2)为Ground。选中该plane对象,在Inspector窗口中,Transform组件的右上方,齿轮按钮,选择Reset,这将会让Ground对象的位置等参数初始化。然后将Transform的Scale参数设置 X=2 Y=1 Z=2。注:plane对象的Y参数只分正负,正数是指向向Y轴正方向的一面可见,背面不可见,负数则相反。
下面是设置Ground的显示效果,材质Material设置。
新建文件夹(此不是必须的),Project窗口Create–Folder创建文件夹,重命名为Materials。
Create–Material创建材质球,重命名为MatGround。选择该材质球,在Inspector窗口,点击MainMaps下的Albedo右边的颜色按钮,配置颜色为RGBA 0 32 64 255.
Unity教程学习_第4张图片
接下来为Ground设置材质球。
方法一:选中Project下的MatGround材质球,按住拖动到Hierarchy下的Ground游戏对象上,之后Ground会变成我们设置的材质。
方法二:选中Hierarchy下的Ground,在Inspector窗口的MeshRenderer组件上,有一个参数Materials,选中,设置其的Size为1,Element0参数设置,点击右边的小圆点,在列表中选择MatGround材质球。

1.4 新建小球与重设位置

Hierarchy窗口Create–3D Objec–Sphere创建一个球的游戏对象,重命名为Player,并Reset初始化。小球理应让其在地面之上,所以我们要设置小球的位置,不改变球的大小(Scale)。因为scale为(1,1,1),而地面位置为(0,0,0),让小球的最小面在地面之上,所以小球应该上移scale的Y的值的一半0.5。故设置position为(0,0.5,0)。

2).小球的移动控制

玩家控制小球在游戏区域内移动,碰到墙壁后不会跑出去,不会飞出去,碰到收集的物体,能收集物体。可以运用unity的物理系统,将用户的输入信息转换到小球控制上。

2.1添加rigidbody

Hierarchy窗口选中Player游戏对象,在Inspector窗口,最下方AddComponent–Physics–Rigidbody,添加刚体组件,因为对象已经有Collider组件(SphereCollider),可以触发物理碰撞等。

2.2添加控制脚本

同样的操作AddCmponent–New Script在弹出的小窗口输入自定义的脚本名称PlayerCtr,选择下方的Create and Add,等新脚本创建后,新建一个文件夹Scritps,然后将PlayerCtr脚本移动到文件夹下,方便控制。
Unity教程学习_第5张图片

2.3小球移动控制

双击PlayerCtr脚本打开编辑,建议使用visualStudio打开,设置请自行上网查找。

2.3.1输入控制

获取用户输入信息,因为我们只需要在平面上移动,所以我们只控制前后左右,上下不做控制。

float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");

Input是Unity的输入系统,关于调用的方法,可以通过visualStudio的联想和unity说明文档查找。GetAxis(string axisName)方法,参数是名称,名称可以在Unity的InputManager中查看,可从以下路径进入,菜单Edit–Project Settings–Input,会在Inspector窗口里显示InputManager设置面板。
moveHorizontal 是水平左右输入值,正表示右,负表示左。
moveVertical是水平前后输入值,正表示前,负表示后。

2.3.2刚体控制

小球移动运用unity物理系统,所以我们通过控制rigidbody,来实现控制小球移动。需要在我们的脚本PlayerCtr中获取刚体组件,对刚体组件操作,可通过查找文档来确定rigidbody调用的方法。

2.3.2.1 获取刚体组件

生成的脚本PlayerCtr继承至MonoBehaviour,存在Start(),Update()方法,这些方法可通过查找MonoBehaviour生命周期了解其调用,触发机制和前后顺序。
Unity教程学习_第6张图片
Start()为脚本调用的第一帧,只会执行一次,我们可以在Start里获取刚体组件。由于后面每一帧都要对刚体进行操作,把刚体变量声明成成员变量是最好的选择。

private Rigidbody RigPlayer;

在Start()中获取组件并赋值

void Start ()
{
  RigPlayer = GetComponent<Rigidbody>();
}
2.3.2.2 刚体运动控制

每一帧都要对刚体进行控制,控制代码放在Update(),或FixedUpdate()中,其中FixedUpdate()适用于物理体系的调用,所以把刚体控制代码放在FixedUpdate()中。
刚体的运动,是给刚体施加推力,查阅可得方法AddForce(Vector3 force, ForceMode mode = ForceMode.Force),或者public void AddForce(float x, float y, float z, ForceMode mode = ForceMode.Force)。
采用前一个,Vector3是3维向量,因为我们用户只有输入左右前后,所以,我们改变x和z,y始终为0,即竖直方向上不给施加额外的推力(rigidbody本身可以设置是否有重力)。同样Vector3我们也可以把它独立成成员变量。

private Vector3 VecDirec = Vector3.zero;
private void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
        VecDirec.x = moveHorizontal;
        VecDirec.z = moveVertical;
        RigPlayer.AddForce(VecDirec);
    }

Play模式测试,会发现运行速度不行,不满足,那我们就需要调整vector3的x和z的值,通过给其一个倍数来控制,为了方便修改,这个倍数用public的成员变量来设置,这样可以在Inspector可视化修改其值。修改后如下。

public float Speed;
private void FixedUpdate()
{
   float moveHorizontal = Input.GetAxis("Horizontal");
   float moveVertical = Input.GetAxis("Vertical");
   VecDirec.x = moveHorizontal*Speed;
   VecDirec.z = moveVertical*Speed;
   RigPlayer.AddForce(VecDirec);
}

Unity教程学习_第7张图片
到此,小球的移动控制已经完成。

3).相机跟随

场景里有个MainCamera游戏对象,其上挂着Camera组件,这是用户看到的画面效果,也就是Game中的效果,我们需要让镜头跟随小球移动。
先调整MainCamera的位置角度,position设置为(0,10,0),旋转设置Rotation为(45,0,0)。
处理方法一,不是合理的方法,但可以提供一个思路,把MainCamera对象拖动到Player小球对象下面,让其依附于小球。但是从Play模式下会发现问题,小球旋转,镜头跟着旋转,这样完成找不到方向。毫无体验感可言。我们的需求只是镜头跟随小球移动,但是镜头不做旋转。所以MainCamrera还是独立,给其添加一个控制脚本来实现该跟随功能。
选中MainCamera游戏对象,AddComponent–New Script输入新脚本名称CameraCtr,将新脚本移至Scripts文件夹,打开编辑。
初始化时先获取方向

	/// 
    /// 小球Player的Transform
    /// 
    public Transform TraPlayer;
    private Vector3 offset;
	// Use this for initialization
	void Start () {
        offset = transform.position - TraPlayer.position;
	}

然后每一帧更新MainCamera的位置,对相机的操作,代码应该放在LateUpdate中。

private void LateUpdate()
    {
        transform.position = TraPlayer.position + offset;
    }

记得将Player小球挂载到MainCamera对象的脚本的的TraPlayer参数上(在MainCamera对象的Inspector窗口)。

4).创建游戏区域

需要4面墙壁来挡住小球,防止小球跑出地面区域,为了方便管理,先创建一个空游戏对象并初始化位置,重命名为Wall。
Create–Create Empty,在Inspector中Transform进行Reset,F2重命名。
Unity教程学习_第8张图片
创建第一个墙壁,Create–3D Object–Cube,初始化位置后,设置scale为(0.5,2,20.5),这是左右墙的设置,重命名为EastwardWall。接下来再Scene视图中,选中该墙壁,选择菜单栏下的第二个按钮,将墙壁移动到合适位置,最后在微调Position为(10,0,0)。把该墙壁移到Wall对象下面。
Unity教程学习_第9张图片
复制墙壁对象(菜单Edit–Duplicate或者选中墙壁,快捷键ctrl+D),修改名称,移动,修改数值,最终形成合围的4面墙,统一放在Wall对象下管理。注意,前后墙壁可以通过改变Rotation的Y为90实现旋转。
Unity教程学习_第10张图片
由于该游戏中,地板和墙壁都是不会动态改变移动,旋转,拉伸的,所以可以设置为静态,以地面为例,选中Ground对象,在Inspector页面,右上方有个static勾选按钮,勾选。或者在其下拉中选择需要的静态选项。
Unity教程学习_第11张图片

5).收集对象

5.1创建收集预制品

收集对象要让用户感觉到,要与其他的做区别,怎么突出显示呢?在不考虑性能情况下,要突显出来。一是收集物设置成运动的,二是颜色区别。
创建收集物体的游戏对象。Create–3D Object–Cube,初始化Reset,重命名为Collection。设置位置,使其悬空,高于地面。设置角度,使其倾斜(45,45,45)。
设置成运动状态。添加一个新脚本RotationCtr,因为没有物理力的作用,所以在Update里更新旋转角度。

 private Vector3 VecRot = new Vector3(15, 30, 45);
 void Update () {
        transform.Rotate(VecRot*Time.deltaTime);
	}

为了让其平滑的运动,每一帧运动的角度要跟每一帧的时间做处理。Time.deltaTime是上一帧到这一帧的时间。
颜色处理。收集物体要跟地面,墙壁,小球做区分,所以最好用不同颜色处理。直接复制原来的地面的材质球,新的材质球更改颜色,在给收集物添加该材质。
Unity教程学习_第12张图片
做成预制品保存,进行复用。在Project窗口新建文件夹Prefabs,然后将Collection收集物体对象拖动到Prefabs文件夹下,这样就会形成一个收集物预制品。复制收集物,调整位置,形成一个圆。
Unity教程学习_第13张图片

5.2收集物体

小球碰到收集物后,收集物被收集,则要从镜头里消失。涉及几点,一碰撞检测,二收集物体的消失控制。

5.2.1碰撞检测

基于unity的物理系统,碰撞检测是通过rigidbody和collider碰撞器检测的。从之前情况看到,小球碰到收集物会被弹开,但是我们需要的是碰到收集物体后,不会影响小球原先的运动,所以在收集物的碰撞器我们需要做额外处理,让其有碰撞的基础,同时又不触发物理碰撞。
其实这就是将碰撞器变成触发器,将BoxCollider上的IsTrigger勾选上,这样该对象就变成一个触发器,不会发生物理的碰撞反弹之类的效果。另外,地板,墙壁之类的一定时碰撞器不是触发器。
Unity教程学习_第14张图片

5.2.2收集物的消失控制

IsTrigger勾选后,变成触发器,我们需要获取触发器被触发的情况,这个可以在小球的脚本上去控制,因为小球挂了rigidbody,小球碰到触发器,会在其下的脚本调用有关触发器的方法,下面这几个是3D 触发器触发的方法,分别是进入触发器时调用,离开触发器时调用,处在触发器中调用且只要在触发器中,会一直调用。

	private void OnTriggerEnter(Collider other)
    {}
    private void OnTriggerExit(Collider other)
    {}
    private void OnTriggerStay(Collider other)
    {}

因为我们在一接触就要让收集物体消失,所以需要Enter的方法。还需要考虑一个,如果又多种触发器,我们怎么区分时碰撞到哪一种,解决方法是,给触发器的游戏对象加上Tag标签,通过对比tag来区分。
Unity教程学习_第15张图片
最后是消失控制,如上图,tag标签上面是游戏对象的名称还有一个勾选,勾选状态就是对象的显示控制。选中,该对象就是显示状态,不选中,就是消失,隐藏状态。
整理一下,就是在小球脚本上对private void OnTriggerEnter(Collider other)方法里处理。其中参数other是小球接触到的触发器,通过other对比tag,方法是Component上的CompareTag(参数)参数是tag的字符串。对符合要求的other,执行消失操作。也就是调用GameObject的SetActive方法。代码如下。

private void OnTriggerEnter(Collider other)
    {
        if(other.CompareTag("Collection"))
        {
            other.gameObject.SetActive(false);
        }
    }

另外,页面上需要做一些处理,新增触发的tag,为收集物体的预制品添加tag。
新增。任意选中一个游戏对象,点击Tag的名称,再下拉菜单中点击Add Tag。。。在tag页面中点击+,新增一个tag,输入新增的名称。
Unity教程学习_第16张图片
在收集物体预制品上,同样点击tag名称,下拉菜单选择刚才新增的tag,注意这个名称要跟脚本的的CompareTag的参数是一样的。

5.2.3物理性能优化

unity的物理系统,对动态和静态的性能消耗是又差别的。动态的物理对象是添加了Collider和rigidbody的游戏对象,静态的是添加了Collider,没有rigidbody且一直在改变(位置,选择,大小拉伸)的游戏对象。
收集的物体对象一直在旋转,但它是静态的,性能上,静态的会将一些信息保存到缓存中,由于一直在运动,所以每一帧都要将信息重新缓存到缓存里,这会造成额外的性能开销。因为动态的,信息一直在动态更新,不会做缓存。为了减少这部分缓存性能开销,我们要把静态的设定,改成动态的。
改成动态的,最简单的是为这些收集物体增加rigidbody,并修改rigidbody的一些设置,使其不会有物理属性,而是作为触发器存在。
方法一:取消rigidbody的UseGravity,这样作为触发器的收集物体就不会因为重力而往下掉落,因为是触发器,会穿过地面。但是这个设置还是会收到物理力的影响。
方法二:勾选IsKinematic,这样物体会变成动态的,不受物理力影响,但是其形状大小等改变之类的,受到脚本影响。通过脚本修改Transform或动画等控制。
至此游戏控制基本完成,剩下分数统计之类的其余功能。

6).分数统计胜利提示

收集了物体后,需要对分数进行统计并显示,全部收集完成后,显示胜利。

6.1分数统计与显示

在Player上的PlayerCtr脚本里对收集的分数进行统计。
声明并初始化。

private int count = 0;

收集到物体后,增加分数,最简单处理,一个收集物体增加1。

private void OnTriggerEnter(Collider other)
    {
        if(other.CompareTag("Collection"))
        {
            other.gameObject.SetActive(false);
            ++count;
        }
    }

分数显示,在Hierarchy窗口,Create–UI–Text。会创建三个对象,Canvas是存放UI的父对象,EventSystem是UI的事件系统。还有一个Text是文本UI。我们把分数放到左上角显示。
首先颜色改为白色。
Unity教程学习_第17张图片
位置设置到左上方,并留一定边距。
Unity教程学习_第18张图片
将文字内容删除。然后再脚本里初始化和显示文字。在PlayerCtr显示设置共有变量。记的要将Text对象拖动到改变量上,挂载上去。

public Text TxtCount;

初始化。

TxtCount.text = "count:" + Count.ToString();

在收集到收集物体后,更新显示内容,因为代码一样,可以抽离出来,处理成独立的方法。在初始化和更新分数时调用该方法。

public void SetCountText()
    {
        TxtCount.text = "count:" + Count.ToString();
    }

6.2胜利显示

在所有收集物体被收集完成后,显示胜利。这需要另外一个文本显示。操作类型,新建Text,设置位置,颜色大小等,脚本控制初始化和胜利显示。

TxtWin.text = string.Empty;

在收集触发方法里,更新胜利显示。

if(Count>=12)
{
   TxtWin.text = "You Win!";
}

功能脚本代码如下

	private int Count = 0;
	public Text TxtCount;
	public Text TxtWin;
	// Use this for initialization
	void Start () {
        RigPlayer = GetComponent<Rigidbody>();
        SetCountText();
        TxtWin.text = string.Empty;

    }
    private void OnTriggerEnter(Collider other)
    {
        if(other.CompareTag("Collection"))
        {
            other.gameObject.SetActive(false);
            ++Count;
            SetCountText();
        }
    }
    public void SetCountText()
    {
        TxtCount.text = "count:" + Count.ToString();
        if(Count>=12)
        {
            TxtWin.text = "You Win!";
        }
    }

注:代码编写完成,记得把对象挂载到对应变量上。

7).打包

菜单File–Build Setttings在页面里点击AddOpenScene或者把目标场景拖到上部的框里,下面根据需求选择平台。各个平台需要设置一些基础信息,请自行查找。

你可能感兴趣的:(Unity)