2022-09-07
1.项目欣赏
点击访问简化版 点击访问提高版
2.项目需求
.创建3D游戏,利用AWSD或方向键控制玩家(小球)吃掉平台中的怪物(菱形方块);每吃掉一个怪物,积分增加1,同时伴随有音效;吃掉所有怪物后,游戏结束,显示获得胜利的字样。
3.操作技能
项目 Project 视图 View
组件 Component 材质 Material
脚本 Script 资源商店 Asset Store
包 Package 发布 Publish
4.开发技能
游戏对象 GameObject 刚体 Rigidbody
碰撞 Collide 用户界面 UI
预制件 Prefab 相机 Camera
天空盒 Skybox 音频 AudioResource
1. 创建3D项目;
2. 根据需要调整自己喜欢的布局layout;
3. 在Game视图中调整分辨率;
4. 创建plane作为桌面desktop,reset属性后,保持默认大小10*10个单位;创建材质球desktop,修改桌面外观[创建材质文件夹material];
5. 对齐视图:便于观察,调整Scene视图为正向视图:z轴朝前;x轴朝右;y轴朝上,如图1; 右键单击相机,选择对其到视图align with view,使得Scene视图和Game视图保持一致;
6. 创建cube为桌面围栏fence,reset属性后,保持默认大小1*1单位;创建材质球fence,为围栏着色;
7.创建预制件:在项Project的Assets中创建预制件文件夹Prefab,将已经设置好的fence对象拖入,自动生成预制件,方便后续的重复使用;保持当前对象不变,在监视视图Inspector中将其隐去;
8. 第一个围栏:从预制件文件夹中拖入一个围栏到场景中,设置围栏大小和位置:x缩放为10倍,z轴前向移动5个单位:移动到地面远端[前];
9. 第二个围栏:保持第一个围栏为选中状态,CTRL+D,复制一个围栏,更名为fence1;保持大小为10倍,z轴后移为-5:移动到地面近端[后];
10. 第三个围栏和第四个围栏:同样的操作,再分别复制俩个围栏,fence2、fence3,设置大小和位置属性:调整到地面的左右两侧;如图2;
11. 创建一个空物体fences,将4个围栏fence、fence1、fence2、fence3拖到fences下面,形成父子化,便于整体管理 [归类]
12. 创建sphere为小球ball,初始化物体transform属性,使其位于世界坐标中心,最后设置y为0.5,使其位于桌面之上;选择一张喜欢的图片,如图3,拖动到材质文件夹,作为外部材质为小球着色,系统会自动创建一个当前图片的材质球文件夹;最终效果如图4。
[]
. 所有资源按照文件夹的形式分类存放;
. 桌面和围栏可以使用自己喜欢的图片作为材质导入并使用;
. 不要在播放模式下编辑场景;
. 为了调整物体属性方便,通常先在Inspector视图中reset一下,使其位于世界中心;
[] 如何调整使得围栏没有缺口? 初步交流并体会将围栏做成预制件的优势;
1. 为玩家小球添加刚体组件rigidbody,使其具有真实世界的物理属性;同时会自动添加碰撞组件sphere collider;
2. 使用键盘事件让小球动起来;创建script文件夹,创建C#脚本move.cs,双击打开链接的外部编辑器editor,添加鼠标事件,参考代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class move : MonoBehaviour
{    
    // 定义刚体变量
    Rigidbody rb;
    void Start()
    {
        // 游戏开始时获取当前游戏对象的刚体
        rb=GetComponent<Rigidbody>();
    }
    
    void Update()
    {
        // 当键盘asdw或方向键按下时,获取水平和垂直方向的移动[-1, +1]
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        // 将获取的位移作为力加到刚体上,使其运动起来
        rb.AddForce(new Vector3(h,0,v));
    }
}
3. CTRL+S保存并返回unity;等系统自动更新完毕后,播放场景,按下A、W、S、E或方向键,查看小球的前后左右滚动效果;根据需要调整力变量的大小;如:rb.AddForce(new Vector3(h,0,v)*5);
[] Input Manager
Edit->Project Setting->Input Manager->Axes,查看水平轴Horizontal和垂直轴的设定Vertical;
[] 获取刚体RigidBody的方法
. 在脚本中定义私有private变量获取:rb=GetComponent<Rigidbody>();
. 在脚本中定义公有public变量,将游戏对象的刚体组件拖动到监视视图的属性窗口获取;
. 在脚本中定义公有public变量,将游戏对象拖动到监视视图的属性窗口,系统会自动获取游戏对象的刚体组件;
[] 开发文档API的使用:Help->Unity Manual或访问Unity官网
1. 利用cube创建怪物monster,设置大小和位置、材质等属性,使其位于地面之上,如图5。怪物材质同样应该放在材质文件夹中;
2. 让怪物动起来:为了增加动态效果,创建一个旋转的脚本rotate.cs,挂载到怪物上,其绕y轴转起来,参考代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class rotate : MonoBehaviour
{    
    void Update()
    {
        // y轴旋转,其他轴保持不变
        transform.Rotate(0,1,0);
    }
}
3.创建怪物预制件:在层级视图Hierarchy中,将怪物拖动到prefab中,系统自动生成一个预制件,拖动若干怪物到场景视图Scene,分别命名为monster1、monster2、等等,并创建一个空游戏对象monsters来包含所有的怪物;如图6;运行游戏 ,查看小球在滚动过程中和怪物的碰撞情况;
1. 碰撞检测:修改玩家小球挂载的脚本move.cs,增加碰撞检测函数如下。返回项目,运行游戏 ,查看console视图中小球和怪物碰撞的情况;
private void OnCollisionEnter(Collision collision)
{
    print(collision.gameObject.name);        
}
2. 为了判断和怪物的碰撞,采用标签的形式标记怪物:选择怪物预制件monster,在监视窗口Inspector中可以看出,当前未设置任何标签unstaged,如图7;单击unstaged,增加一个标签,标签名为mon,并选择覆盖override,将对预制件的修改应用到其他复制件上。在层级视图Hierarchy中查看,所有的怪物都有了mon标签;如图8,图9,图10;
3. 碰撞消灭:修改move.cs,当碰撞的对象标签是mon时,销毁对象;运行游戏 ,查看消灭怪物的情况;
private void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.tag == "mon") {
        Destroy(collision.gameObject);
    }
}
4. 触发检测:改进碰撞检测,使过程更顺畅:当前的效果是先碰撞再消灭,所以小球在消灭的过程中会有一个卡顿。应该是在碰撞之前,检测到碰撞就开始消灭。 a. 激活怪物的碰撞触发:选择怪物预制件,激活碰撞触发,并应用override到所有的怪物; b. 修改move.cs:删除或注释掉之前的碰撞检测函数OnCollisionEnter;添加碰撞触发函数OnTriggerEnter;注意函数的形式参数发生了变化,相应的属性获取也不一样;再次运行游戏 ,玩家小球消灭怪物的过程就比较正常了;
private void OnTriggerEnter(Collider other)
{
    if (other.tag == "mon")
    {
        Destroy(other.gameObject);
    }
}
[]
1. 分布创建plane、cube和sphere,默认都添加了碰撞组件;调整位置和大小,使得cube、sphere位于plane之上;
2. 为sphere添加刚体,创建脚本并挂载碰撞检测过程提供的代码;
3. 运行游戏并在场景视图Scene中拖动sphere撞击cube,查看碰撞的过程;
4. 为脚本添加碰撞触发检测过程提供的脚本;勾选cube碰撞组件的:Is Trigger选项;
5. 再次运行游戏,在场景视图Scene中拖动sphere撞击cube,查看碰撞触发检测的过程;
6. 取消cube的碰撞组件,再次查看碰撞检测和碰撞触发检测的情况;
[碰撞检测过程]
碰撞开始enter -> 碰撞持续stay -> 碰撞结束exit
private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "cube")
        {
            print("collision enter");
        }
    }
    private void OnCollisionStay(Collision collision)
    {
        if (collision.gameObject.tag == "cube")
        {
            print("collision stay");
        }
    }
    private void OnCollisionExit(Collision collision)
    {
        if (collision.gameObject.tag == "cube")
        {
            print("collision exit");
        }
    }
[碰撞触发检测过程]
进入碰撞检测区域enter -> 持续处于碰撞检测区域stay -> 离开碰撞检测区域exit
private void OnTriggerEnter(Collider other)
    {
        if(other.tag == "cube") {
            print("trigger enter");
        }
    }
    private void OnTriggerStay(Collider other)
    {
        if (other.tag == "cube")
        {
            print("trigger stay");
        }
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "cube")
        {
            print("trigger exit");
        }
    }
[] 碰撞组件:是给别人用来做碰撞检测或触发检测的,不是给自己用的;为了更真实的反应物理现象,双方都需要有碰撞组件;游戏对象创建时默认都带有碰撞组件;碰撞区域bounding的选择和编辑;
[] 碰撞检测;
[] 碰撞触发检测;
[] 利用标签识别游戏对象;
[] 游戏对象销毁Destroy;
Declaration
public static void Destroy(Object obj, float t = 0.0F);
Parameters
obj The object to destroy.
t The optional amount of time to delay before destroying the object.
[] 开发文档API的使用:Help->Unity Manual或访问Unity官网
[] 体会碰撞的几个阶段;体会标签的作用;
1. 添加UI中的text组件[传统legacy类],层级Hierarchy视图会额外增加画布canvas和相应的事件处理系统EventSystem; 画布自动覆盖游戏窗口,播放时将出现在游戏画面上层,用于引导玩家。为了便于观察,双击画布canvas,使其位于屏幕中心,同时调整场景视图Scene模式为2D。
2. 选择text组件,命名为grade,同时在监视视图Inspector中修改其字体、大小、颜色等属性。
3. 位置调整:将text组件grade放于屏幕中心:组件中心和定位标记重合。快速定位:选择grade组件,单击定位框,按住ALT,选择水平居中和顶部居中图例,或根据需要可以调整到其他位置;如图11、图12;
4. 修改玩家小球的脚本move.cs,增加计分的逻辑;保存并返回游戏;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class move : MonoBehaviour
{
    // 定义刚体变量
    Rigidbody rb;
    public Text scoreTexts;
    int score=0;
    void Start()
    {
        // 游戏开始时获取当前游戏对象的刚体
        rb=GetComponent<Rigidbody>();
    }

    void Update()
    {
        // 当键盘asdw或方向键按下时,获取水平和垂直方向的移动
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        // 将获取的位移作为力加到刚体上,使其运动起来
        rb.AddForce(new Vector3(h,0,v)*5);
    }
    
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "mon")
        {
            score++;
            scoreTexts.text=score.ToString();
            Destroy(other.gameObject);
        }
    }
}
5. 先选择玩家小球,再把grade组件拖到小球ball监视视图inspector中,move.cs脚本所定义的scoreTexts得分属性框。运行游戏 ,查看得分情况;
[] 传统UI的使用;
[] 游戏对象的获取;组件的获取;
[] 变量的声明、初始化和传递;
[] 体会UI的使用;
1. 胜利提示信息:使用同一个text;使用另外一个text,一开始隐藏,当胜利后显示出来。提示:利用游戏对象GameObject的API实现:SetActive(true/false);
2. 音频组件Audio Resource;点击下载音效文件资源包或自行查找;
3. 相机跟随:保持相机和玩家小球的相对位置position不变;参考代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class camFollow : MonoBehaviour
{
    public Transform ball;
    Vector3 offset;
    void Start()
    {
        offset=transform.position-ball.position;
    }

    void Update()
    {
        transform.position=ball.position+offset;
    }
}
4. 天空盒的使用:点击访问资源商店Assets Store查找;点击下载天空盒资源包;个人分享收集;
File -> Build Settings...
1. 添加场景Scenes In Build;
2. 选择平台并切换Switch Platform;
3. 其它特别选项设置,如WebGl的播放选项Player Setting...;
4. 发布或发布并预览Build 或Build And Run;
[]平台切换过程中,可能会提示下载对应的扩展包;
开发技能 Skills
. 基本视图:场景Scene、层级Hierarchy、游戏Game、项目Project、调试Console
. 基本游戏物体:cube、sphere、plane
. 常见组件:变换、材质、刚体、碰撞、脚本
. 预制件:open修改;override修改
. 脚本控制游戏对象/组件的基本思路、变量的传递
. 脚本常见生命周期函数:Start()、Update()
. 常用方法:Translate()、Rotate()、addForce()
. UI:Text
. 发布Building:桌面端Window、安卓Android、移动端网页WEBGL
计算思维 Computing
架构设计
流程控制
规范开发
团队合作
作业讲评
1. 未按要求提交:格式错误;内容缺失;内容错误;
2. 文件、资源命名不规范;没有合理利用文件夹收纳归类;
3. 体积过于庞大;
4. 设计感体现不够;
5. API过时;
CS0619 “GUITexture”已过时:“GUITexture has been removed. Use UI.Image instead.”
CS0619 “GUIText”已过时:“GUIText has been removed. Use UI.Text instead.”