从零开始Unity引擎学习

简单说明

你好! 这是我从零开始学习unity的博客。
每有空就会添加一个小标题用于总结当天所学的知识。

在小标题下会用加粗字体一句话总结当日学习的知识点。
刚开始接触unity什么都不懂,有错误还请多多包涵( ゚Д゚)ノ

教程

unity核心类图
官方用户手册

【翻译注释文档】
游戏蛮牛Unity 用户文档1.0

代码调试和配置环境
调试C#代码

【Unity界面基础认识①】2021/12/13

本日安装了Unity 和 Unity Hub 。
学习到了:初步认识了unity界面、学习了什么叫场景,如何新建场景、场景编辑器、如何添加资源、什么叫游戏对象,坐标系。

1.unity界面初识
新建项目后安装unity界面如图所示。
unity界面

Project项目面板:项目内的资源文件。图中下方的面板。可在里面直接建立场景、文件夹、对象、脚本等等物品
Scene场景视图:以可视化的形式展现游戏内容。
面板内部左上角一排工具分别为:抓取工具(可拖动视图)、旋转平移工具、自由变换工具和拉伸工具。其中旋转可进行xyz轴的旋转。
右上角工具为相机和将视图画面2D3D切换的可视化工具。
Game游戏视图:对游戏进行播放预览。
Hierachy层级面板:以树状形式显示游戏中的对象(左侧面板)
inspector 组件面板:显示一个游戏对象的全部组件
Console控制台:打印调试的

2.什么叫场景,如何新建
场景就是一个游戏里的关卡,或者某个画面。
我个人理解,以找茬游戏为例,应该是一个解密里的一个关卡,在该关卡中进行解密,到下一关就切换为下一个场景。

扫描二维码关注公众号,回复: 14919552 查看本文章

新建场景:在project面板中右键create新建场景就可以了。

3.场景编辑器
就是中间这个编辑窗口,可以用滚轮放大缩小,也可以平移。
其内部可见:
1.网格: 一个占屏幕上100像素、1个Unit(可自定义)
2.主摄像机:摄像机的可视范围
3.世界坐标系:x,y轴

4.如何添加资源
方法:
①直接将素材拖拽进project面板的Assets
②右键Assents区域选择import导入

注:可直接右键素材Show In Explorer找到该素材所在的目录,当导入素材后会多出一个对应的.meta文件。且删除原有的素材项目内素材不会消失,因为unity导入规则是复制一份新的文件
管理资源时可以添加子目录来分类管理。可以重命名素材,也可以删除素材。

5.游戏对象,坐标系的概念

游戏对象就是场景中包含的内容。
比如将某个图片拖进Scene窗口或是Hierarchy窗口中,就新增了一个游戏对象。
点击该对象,右侧会显示该对象的属性信息(名字,变换信息、组件,脚本情况等等)

注:在场景中只是保存了游戏对象引用了谁,比如说引用了某个图片,该图片的id是什么。

坐标系可以在对象的Inspector属性面板查看对象的坐标xyz轴
坐标的单位:一个方格是一个Unit(单位),一个单位在现实世界中都可以自行约定。
x轴:水平向右
y轴:垂直向上
z轴:垂直屏幕向内
(可以用ae的摄像机来理解)

【Unity基础界面认识②】2021/12/14

今日学习:认识了游戏对象的概念及其操作和显示顺序、摄像机、组件。

1.游戏对象

基本操作
移动(更改position值或者使用工具)
旋转(x,y,z轴都可以作为旋转轴)
缩放(x,y轴的缩放,中间的方块可以不失去比例的缩放)
矩形工具:自由变换(按shit键可以等比例缩放)
隐藏和显示:属性中选择active复选框

点击 Sprite Editor打开图形编辑器,右下角显示图片的各个属性

名称 作用
name 名字
Position centered 位置
Border
Pivot 轴心(可选择轴心位于哪里)
Pivot Unit Mode
Custom Pivot

图片切割
打开图片编辑器后,打开左上 第二个菜单:Slice,点击Slice,会自动剪裁图片,分成相应的多个图片游戏对象

图片与渲染器
Sprite:精灵
Sprint Renderer:图片渲染器,用于显示一个Sprite(一种组件)
将素材拖拽到Sprite的位置,替换当前显示的素材图片。

显示顺序
-修改Order in Layer属性
-修改Z坐标

父子关系
与AE的父子关系同理,子对象随父对象变换
:在树状管理器里拖拽就可以设置了

2.摄像机
点击摄像机可查看其添加的组件
下面展示了部分了解到的组件

名称 功能
Transform 一种组件,表示:Position:位置 Rotation:旋转 Scale:缩放(每个游戏对象都有的组件)
Background 背景,显示区域外的填充是什么颜色
Size 摄像机的范围大小,默认为5个单位,描述了摄像机可以拍摄多大的区域

Game窗口中可以跳调整其长宽比例

3.组件
组件:用于实现一种功能。
在右侧面板种可以添加一些组件

【Unity 脚本认识①】2021/12/15

今日学习:认识了什么叫脚本、如何挂载脚本、学习查看手册、认识了脚本中的Start()和Update()

什么是脚本
游戏对象的行为由附加的__组件__控制。脚本可以理解就是在写一个自用组件(?

怎么挂载脚本
Assets窗口创建一个C#脚本
双击脚本启动VS
挂载脚本01
在VS页面内可以编辑该脚本,编辑脚本后退出到unity界面,可以直接将脚本拖拽至对象上挂载,然后点击Game窗口中运行。

①新建一个脚本和使用它
新建一个脚本后点开脚本观察其代码。

using UnityEngine;
using System.Collections;

public class MainPlayer : MonoBehaviour {

    // 使用此函数进行初始化
    void Start () {
    
    }
    
    // 每帧调用一次 Update
    void Update () {
    
    }
}

为了连接到 Unity 的内部架构,脚本将实现一个类,此类从称为 MonoBehaviour 的内置类派生而来。也就是说MonoBehaviour是每一个脚本的基类。

MonoBehaviour 是一个基类,所有 Unity 脚本都派生自该类。
使用 C# 时,必须显式派生自 MonoBehaviour。(不太理解,找个例子来理解它)

——什么叫做显式派生?
就是在派生类的构造函数中指定要调用的基类的构造函数,
并将派生类构造函数的部分参数值传递给基类构造函数。
——什么叫做隐式派生?
隐式方式就是在派生类的构造函数中不指定对应的基类的构造函数,
这个时候调用的是基类的默认构造函数。

monoBehaviour介绍及其函数

Component 菜单上的 Scripts 子菜单,其中包含项目中可用的所有脚本,包括自定义的脚本。

每当创建一个脚本时,自动创建一个类.cs文件,文件名与类名相同。
会由引擎自动调用:

Start() :启动时调用
Update():每帧调用 (帧率不固定)

内部执行的顺序:
1.创建对象
2.创建其调用的组件
3.Start()
4.定时执行Update()

【Unity 脚本认识②】2021/12/16

今日学习:重新配置了环境、学习帧率的概念、如何获取当前对象和其他对象的组件

今早打开脚本不知道为什么vs的自动补全代码又没了,就把环境重新配置了一下。
查看vs2019的unity相关组件是否装上。(查看发现明明已经安装好了T T)
然后打开unity,点击edit中的preference,找到自己安装的编译器,进行一个选择。
配置
然后记得一定要重启,再次打开后双击脚本就能查看代码的补全了。

②什么叫帧率,如何让一个物体匀速移动?
帧率就是指每秒钟更新多少次。
FPS = 50 约每20ms 更新一次
FPS = 60 约每16.7ms更新一次

Debug.log("In Update() .." + Time.delaTime);

观察间隔多久刷新一次
控制台情况

提问,什么叫做delaTime?
中文名称增量时间,含义是每经过一帧所耗费的时间。
Update()每帧执行一次,那么经历过一帧耗费的时间就是 增量时间(deltaTime)
1秒内Update执行的次数,就是1秒内执行的总帧数

举个例子

 void Update()
    {
    
    
        transform.Translate(0, 0, Time.deltaTime * 10); //物体沿着自身Z轴方向,每秒移动物体10米运动
    }

用增量时间*10,可以让物体每一秒钟移动10米。而Update()每一秒刷新的次数都不同。

(Update执行一次就是一帧,每秒的帧不同但每一帧移动的距离都是速度 * 时间,最后相当于再乘每秒钟有几帧,相当于下面这个公式)

10米=(增量时间 * 1秒总帧数) *10米/秒

③如何获取节点和组件?

-当前游戏对象

this.gameObject //当前你这个游戏对象

取得当前对象的全部组件

    void Start()
    {
    
    
        //在当前对象下寻找类型为SpriteRenderer类型的全部组件
        SpriteRenderer render = this.gameObject.GetComponent<SpriteRenderer>();
        //简写:SpriteRenderer 自变量名称 = this.GetComponent<获取的类型>();
        render.flipY = true; //对当前对象进行一个翻转(X对称轴)
    }

运行脚本后图片翻转了……
图片翻转

-获取其他对象

 GameObject obj = GameObject.Find("相应对象的路径");

【Unity 脚本认识③】2021/12/17

今日学习:父子节点关于transform的一些简单操作、组件的属性如何增加修改,游戏对象如何改变坐标,旋转。

今天又有问题了。
在这里插入图片描述
找到这个用户目录下,删除原有的许可证。
目录
再重启电脑,重新打开unity hub申请许可证就好了。

④父节点和子节点
左侧树状文件结构由Transform来管理。
常见操作:
1.取得父节点,获得父节点的名称

    //取出当前对象的父节点
        GameObject parentobj = this.transform.parent.gameObject;
        Debug.Log("rabbit的父节点" + parentobj.name);

2.遍历全部子节点
遍历当前对象的全部子节点并打印出其名称。

		//遍历子节点
        foreach (Transform child in transform)
        {
    
    
        Debug.log("子节点" + child.name);
        }

3.把一个节点移动到另外一个位置。

将rabbit对象挂载到主摄像机对象下面。

    //分别定义两个对象
        GameObject obj = GameObject.Find("rabbit");
        GameObject target = GameObject.Find("Main Camera");

        //通过transform,先找到子节点的transform,将其挂到父节点的transform下面
        obj.transform.SetParent(target.transform);

未运行前层级关系。
在这里插入图片描述
运行后层级关系。
在这里插入图片描述
当想将对象挂载到场景下面的时候,直接设置为null

 obj.transform.SetParent(null);

游戏运行时,层级面板和组件面板都会显示实时数据。

⑤组件的属性定义
在组件面板中可以看到组件下面的一些属性。

新建一个脚本,在脚本内定义一个public的组件属性,我们可以看到属性面板中显示了我们自己定义的属性。
在这里插入图片描述
该值也可以修改
在这里插入图片描述
⑥游戏对象运动计算

Vector3,用于表示一个3维向量(x,y,z),也称为3元数。

定义一个三维的坐标,坐标表示为浮点数,所以在其后必须要加f

 Vector3 pos = new Vector3(0, 0.1f, 0);

节点坐标、旋转都可以用Vector3表示

transform.position = new Vector3(0, 0.1f, 0); //设置位置
//eulerAngles 欧拉角 用来旋转的 旋转了45度
transform.eulerAngles = new Vector3(0, 0, 45f); 

世界坐标 World Space Position 绝对坐标,以世界坐标系计算
本地坐标 Local Space Position 以父节点的坐标系计算

控制游戏对象在一定范围内移动,当碰到上下边界的时候就调转自己的方向。

 private bool upward = true;//飞行的方向,向上
    // Update is called once per frame
    void Update()
    {
    
    
        if(upward && transform.position.y > 5)
        {
    
    
            upward = false;
            transform.localEulerAngles = new Vector3(0, 0, 180);
        }
        if (!upward && transform.position.y < -5)
        {
    
    
            upward = true;
            transform.localEulerAngles = new Vector3(0, 0, 0);
        }

        float step = 1.6f * Time.deltaTime;
        transform.Translate(0, step, 0, Space.Self);
        
    }

【Unity 脚本认识④】2021/12/19

今日学习:向量的概念,向量标准化,向量算数,屏幕坐标的概念

⑦向量的概念,向量标准化,向量算数

求向量长度的API
float len = v.magnitude

标准化:将向量的长度缩放为单位向量(长度为1的向量)

Vector3 b = a.normalized;

几个常用标准向量:

Vector3.right,即Vector3(0, 1, 0) x轴
Vector3.up,即Vector3(1, 0, 0) y轴
Vector3.foward ,即Vector3(0, 0, 1) z轴

两物体间距离:求两物体位置表示出的向量,然后计算向量长度,就可以得出物体间距离。

Vector3 direction = p2 - p1;
direction.magnitude//物体间的距离

Vector3.SignedAngle(b,a,Vector3.forward);//计算a到b的角度,旋转轴为z

使一个对象跟随另外一个对象的移动而转移自己的朝向。

    void Start()
    {
    
    
        Vector3 face = this.transform.up;   // 获取朝向
        GameObject target = GameObject.Find("音符");
        Vector3 direction = target.transform.position - this.transform.position;
        //计算两对象间的向量
        float angle = Vector3.SignedAngle(face, direction, Vector3.forward);
        //计算向量与当前对象的角度
        this.transform.Rotate(0, 0, angle); //旋转这个角度
    }

⑧屏幕坐标
就是游戏对象显示在当前屏幕上的位置。

Screen.width(像素)
Screen.height(像素)

获得一个物体的屏幕坐标(将世界坐标变成屏幕坐标)

Vector3 worldpos = transform.position;
Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
屏幕的边界(可以用屏幕坐标来控制游戏对象不超出屏幕的边界)

【Unity 脚本认识⑤】2021/12/20

今日学习:获取鼠标事件,游戏对象跟随鼠标,鼠标拖拽功能的实现*

⑨获取鼠标事件,游戏对象跟随鼠标

Input.GetMouseButtonDown(0)//获取鼠标左键是否按下
Input.mousePosition;//获取鼠标当前的位置

0表示左键,1表示滚轮,2表示右键


探测鼠标位置在哪里,先取其屏幕位置,再转换为世界位置。

      // 探测 ‘鼠标左键按下’ 事件是否发生
        if (Input.GetMouseButtonDown(0))
        {
    
    
            Debug.Log("鼠标按下,pos=" + Input.mousePosition);

            Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            Debug.Log("世界坐标: " + pos);
            pos.z = 0;    // 把Z坐标置0,放到2D平面上来
        }

跟随鼠标移动,并改变自己的方向

    void Start()
    {
    
    
        Application.targetFrameRate = 60;
    }

    // Update is called once per frame
    void Update()
    {
    
    
        if (Input.GetMouseButton(0))    //检测鼠标位置
        {
    
    
            //获取当前鼠标的屏幕坐标转换为世界坐标
            Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            pos.z = 0;
            //改变方向
            SetDirection(pos);
        }
        //控制移动
        float step = 5f * Time.deltaTime;
        transform.Translate(0, step, 0, Space.Self);
    }

    //变换其方向
    void SetDirection(Vector3 targetPos)
    {
    
    
        Vector3 face = transform.up;    //记录当前游戏对象位置
        Vector3 direction = targetPos - transform.position;//算出两向量位置间的向量

        float angle = Vector3.SignedAngle(face, direction, Vector3.forward);//计算其夹角
        transform.Rotate(0, 0, angle);//改变当前游戏对象的方向

    }

效果如图所示

⑩鼠标拖拽功能的实现
鼠标点击拖动游戏对象。


    private bool drag = false;  //是否被拖拽
    private Vector3 lastMousePos;//鼠标的相对位移

    // Start is called before the first frame update
    void Start()
    {
    
    
        Application.targetFrameRate = 60;
    }

    // Update is called once per frame
    void Update()
    {
    
    
        //当鼠标按下的时候
        if (Input.GetMouseButtonDown(0))
        {
    
    
            Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            pos.z = 0;    // 把Z坐标置0,放到2D平面上来

            //判定鼠标离游戏的点有多远
            float distance = (pos - transform.position).magnitude;//求出两者之间的距离
            if (distance < 1.5f)//如果该距离小于1.5的时候
            {
    
    
                drag = true;
                lastMousePos = pos;//使游戏对象跟随鼠标移动(拖拽)
            }
        }

        //没有按下的时候就不移动
        if (Input.GetMouseButtonUp(0))
        {
    
    
            drag = false;
        }


        // 当拖拽的时候
        if (drag)
        {
    
    
            //先查找鼠标在世界坐标中的位置
            Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            pos.z = 0;    

            //计算现在鼠标位置相对于之前鼠标位置的距离
            Vector3 delta = pos - lastMousePos;
            
		      //根据相对移动距离进行一个加法的计算(移动)
            transform.position += delta;   
            

            lastMousePos = pos;//更新鼠标的新位置
        }
    }

实现效果

【Unity 脚本认识⑥】2021/12/21

今日学习:什么是事件函数,脚本参数,认识预制体

⑪事件函数(Event Fuction),脚本执行顺序

由系统主动调用的
Awake(),Start(),Update(),FixedUpdate()

相应事件的回调函数On开头的
OnEnable(), OnDisable(), OnGUI()…

Awake()→OnEnable→Start()→…
可以把初始化放在Awake或者Startl里面。

多个附加脚本的游戏对象同时执行(是无序的,优先级均为0)
可以用 Execution Order来判定脚本的执行顺序 : 优先级
顺序值越小,优先级越高。
一个脚本优先于其他脚本,执行顺序为-1;
一个脚本晚于其他脚本,执行顺序1;

脚本的参数:就是在脚本里的函数定义的变量,定义后可在组件面板看到定义的参数。

值类型(C#中的struct,也就是Vector3),不可以初始化为null。

小练习:点击切换图片

    public Sprite sprite0;
    public Sprite sprite1;
    private int index = 0;
    void Start()
    {
    
    
    }
    void Update()
    {
    
    
        if (Input.GetMouseButtonDown(0))
        {
    
    
            DoChange();
        }

    }
    void DoChange()
    {
    
    
    	//使用这个Render组件
        SpriteRenderer renderer = GetComponent<SpriteRenderer>();
        if(index == 0)
        {
    
    
            index = 1;
            renderer.sprite = this.sprite1;
        }
        else
        {
    
    
            index = 0;
            renderer.sprite = this.sprite0;

        }
       
    }

⑫预制体

预制体:Prefab, 预先制作好的物体(模板),一般用于游戏对象的动态创建。
预制体实例:Prefab instance

建立预制体后,可通过拖拽新增预制体的实例。
双击点击要修改的预制体进入预制体编辑界面,可给预制体挂载脚本,同时预制体的实例也会受到该脚本控制。
点击某一预制体后,右上角面板如图所示。

Open为编辑预制体
Select找到当前预制体实例的预制体
Override 是否将预制体实例的修改反过来修改到预制体上。

如何使预制体实例与其预制体断开关联?
右键该对象,选择Unpack Prefab

⑬动态创建与销毁预制体
当鼠标点击时,miku创建一个预制体音符,音符飘出去,并在超出屏幕范围时销毁自身。

mikumove.cs

    public GameObject myPrefab;
    void Update()
    {
    
    
        if (Input.GetMouseButtonDown(0))
        {
    
    
            Sing();
        }
    }
    private void Sing()
    {
    
    
        GameObject music = Instantiate(myPrefab);
        music.transform.position = transform.position + new Vector3(0, 1f, 0);
    }

在界面中将音符对象挂载到miku的预制体对象实例上。

  // Update is called once per frame
    void Update()
    {
    
    
        float step = 1.5f * Time.deltaTime;
        transform.Translate(0, step, 0, Space.Self);

        Vector3 sp = Camera.main.WorldToScreenPoint(transform.position);
        if(sp.y > Screen.height)
        {
    
    
            Destroy(this.gameObject);
        }
    }

【Unity 脚本认识⑦】2021/12/22~23

今日学习:定时器、认识Unity中重要的类

    //创建一个预制体资源音符
    public GameObject myPrefab;

    private float interval = 0.4f; //每隔0.4秒发射一颗
    private float count = 0;

    void Update()
    {
    
    
        //计时
        count += Time.deltaTime;
        if(count >= interval)//如果当前累计的事件到了0.4秒
        {
    
    
            count = 0;//清零
            Sing();//进行一个歌的唱
        }
  
    }

    private void Sing()
    {
    
    
        Vector3 pos = transform.position + new Vector3(0, 1f, 0);
        GameObject bullet = Instantiate(myPrefab, pos, transform.rotation);
    }

效果如图

最近几天学学校的项目比较忙,所以看了一点别的,这里列一下unity中比较重要的类,复习一下之前学的一些方法和语句。

类名 描述
GameObject 表示可以存在于场景中的对象的类型。
MonoBehaviour 基类,默认情况下,所有 Unity 脚本都派生自该类。
Object Unity 可以在编辑器中引用的所有对象的基类。
Transform 提供多种方式来通过脚本处理游戏对象的位置、旋转和缩放,以及与父和子游戏对象的层级关系。
Vectors 用于表达和操作 2D、3D 和 4D 点、线和方向的类。
Quaternion 表示绝对或相对旋转的类,并提供创建和操作它们的方法。
ScriptableObject 可用于保存大量数据的数据容器。
Time(以及帧率管理) Time 类用于测量和控制时间,并管理项目的帧率。
Mathf 一组常见的数学函数,包括三角函数、对数函数以及游戏和应用开发中常用的其他函数。
Random 提供简便的方法来生成各种常用类型的随机值。
Debug 用于可视化编辑器中的信息,这些信息可以帮助您了解或调查项目运行时发生的情况。

Quaternmion中常用尤拉角(以下概念理解来自别人的博客)

欧拉角代表一系列的三维基本旋转, 也就是围绕一个坐标系的各轴的一系列旋转。例如,首先绕Z轴旋转一个α角,然后绕x轴做一个β角的旋转,最后再绕Z轴来一个γ角的旋转。这些旋转都是从一个已知的标准方向上进行的。在物理中,

这个初始化的标准方向通常是一个静止的坐标系,在线性几何中,通常是一个标准基。

欧拉角的使用:(此处有待理解。。。比如万向锁)

// 正确使用欧拉角的旋转脚本。
// 将欧拉角存储在一个类变量中,并仅使用
// 该变量作为欧拉角进行应用,但从不依赖于读回欧拉值。
        
float x;
void Update () 
{
    
    
    x += Time.deltaTime * 10;
    transform.rotation = Quaternion.Euler(x,0,0);
}

错误使用:

// 旋转脚本错误 #1
// 此处的错误在于我们正在修改四元数的 x 值
// 此值不表示角度,不会产生所需的结果
    
void Update () 
{
    
    
    var rot = transform.rotation;
    //这里rot定义为var对象,不可以用在旋转角度上(?
    rot.x += Time.deltaTime * 10;
    transform.rotation = rot;
}
// 旋转脚本错误 #2
// 从四元数读取、修改并写入欧拉值。
// 因为这些值是从四元数计算的,
// 所以每个新的旋转可能会返回非常不同的欧拉角,而这可能会受到万向锁的影响。
        
void Update () 
{
    
    
    var angles = transform.rotation.eulerAngles;
    angles.x += Time.deltaTime * 10;
    transform.rotation = Quaternion.Euler(angles);
}

【Unity 脚本认识⑧】2021/12/26

今日学习:认识键盘事件、认识unity里的刚体、碰撞检测的组件使用

键盘事件

Input.GetKeyDown(key) 按下事件
Input.GetKeyUp(key) 抬起事件
Input.GetKey(key) 状态检查是否被按下

物理系统
可以给游戏对象增加一个Physic 2D组件 RigidBody 2D,然后可以设置为物理学刚体还是运动学刚体

刚体:
Dynamic 普通刚体 有质量、有速度
Static 静态刚体,质量无穷大、无速度
Kinematic 运动学刚体,无质量

刚体的碰撞
1)Dynamic 物理学碰撞检测
可以添加Box Collider 2D(碰撞组件),两个刚体会计算其碰撞。
可以编辑刚体的范围

2)Kinematic 运动学刚体
碰撞检测过程:
a. 添加两个物体,‘飞机’ 和 ‘小球’
b .添加刚体组件 Rigidbody 2D
设为 Kinematic

c.添加 碰撞组件 Box Collider2d
勾选 Is Trigger ( 碰撞触发器 )

d.添加脚本组件 , 重写事件函数OnTriggerEnter2D()
void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("飞机:探测到了碰撞 … ");
}

当系统检测到碰撞的时候,会触发碰撞事件的回调

OnTriggerEnter2D: 两个碰撞体开始相遇
OnTriggerStay2D: 两个碰撞体接触中
OnTriggerExit2D: 两个碰撞体分开

碰撞事件的参数:

collision.gameObject 对方碰撞体
collision.transform 对方的Transform组件
collision.name 对方的节点名称
collision.tag 对方节点的Tag(身份) 用于识别碰撞对象的身份(很重要)

Project setting中
a.可以设置当前项目的tag和layer,随后点开某一游戏对象的组件面板时,可以看到当前游戏对象的Tag。
b.Physics 2D中查看Layer Collision Matrix可以设置不同层才会碰撞,相同层的游戏对象不发生碰撞(区别友军和敌军) 使用这个可以减低资源的消耗,因为相同层的对象不会检测碰撞,而上面那个区分对象的方法不管什么tag(身份)都会进行碰撞的检测。

【做一个简单的飞机打怪兽小游戏①】简陋实现

①将界面改为竖屏模式
Window→Layout→Tall(竖屏)
分辨率设置为9:16
在这里插入图片描述

②添加主控脚本
添加空节点“游戏主控”
新建主控脚本MyGame.cs

主控脚本内设置一些全局变量,比如帧率就在主控脚本内设置。

③制作子弹和飞机

将子弹素材制作为预制体,并挂载相应的脚本,并添加碰撞有关组件,当子弹碰上怪物时,使怪物被消灭

public class bullet : MonoBehaviour
{
    
    
    public float speed = 5.5f;
    void Start()
    {
    
    
        
    }

    void Update()
    {
    
    
        float dy = speed * Time.deltaTime;

        transform.Translate(0, dy, 0, Space.Self);

        Vector3 sp = Camera.main.WorldToScreenPoint(transform.position);
        if(sp.y > Screen.height)
        {
    
    
            Destroy(this.gameObject);
        }
        
    }
    //当
    private void OnTriggerEnter2D(Collider2D collision)
    {
    
    
        if(collision.tag.Equals("Monster"))
        {
    
    
            Destroy(collision.gameObject);
            Destroy(this.gameObject);
        }
    }
}

飞机相关代码下所示,通过wasd键可以操控飞机移动(尚未优化屏幕边界问题)
将子弹作为预制体,由飞机发射,标签为Bullet。

public class MyJet : MonoBehaviour
{
    
    
    public GameObject bulletPrefab;
    public float moveSpeed = 2.5f;
    private float interval = 0.4f;
    private float count = 0;


    void Start()
    {
    
    
        Application.targetFrameRate = 60;
      
    }

  
    void Update()
    {
    
    
	 count += Time.deltaTime;
        if (count >= interval)
        {
    
    
            count = 0;
            Fire();
        }
        //控制飞机移动
        Vector3 jp = Camera.main.WorldToScreenPoint(transform.position);
        if (Input.GetKey(KeyCode.A) && jp.x > 0) 
        {
    
    
            transform.Translate(-0.05f,0, 0, Space.Self);
        }
        if (Input.GetKey(KeyCode.D) && jp.x < (Screen.width  ))
        {
    
    
            transform.Translate(0.05f, 0, 0, Space.Self);
        }
        if (Input.GetKey(KeyCode.S) && jp.y > 0)
        {
    
    
            transform.Translate( 0, -0.05f, 0, Space.Self);
        }
        if (Input.GetKey(KeyCode.W) && jp.y < (Screen.height ))
        {
    
    
            transform.Translate(0, 0.05f, 0, Space.Self);
        }



    }
    private void Fire()
    {
    
    
        Vector3 pos = transform.position + new Vector3(0, 1f, 0);   //子弹出现的位置
        GameObject bullet = Instantiate(bulletPrefab, pos, transform.rotation);
    }
}

⑤制作怪物,随机生成怪物
怪物代码如下,让怪物像子弹一样沿着屏幕y轴移动,制作为预制体,标签为Monster。

public class Monster : MonoBehaviour
{
    
    
    public float speed = 1.0f;
    void Start()
    {
    
    
    }
    void Update()
    {
    
    
        float dy = speed * Time.deltaTime;
        transform.Translate(0, -dy, 0, Space.Self);
    }
}


由主控脚本随机生成怪物:Monster_control,建立了一个头像数组,录入后通过改变预制体的图像来随机生成不同的怪物。

public class Monster_control : MonoBehaviour
{
    
    
    public GameObject monsterPrefab;
    public Sprite[] images; //头像的数组
   
    void Start()
    {
    
    
        //反射机制:输入名字找到方法
        //参数的名字(字符串)    多少时间后执行第一次     每隔几秒执行一次
        InvokeRepeating("CreateMonster", 0.1f, 2f); 
    }
    void Update()
    {
    
    
    }
    void CreateMonster()
    {
    
    
        //随机
        float x = Random.Range(-2, 2);//限定生成怪物的范围
        float y = 5;

        //初始化怪物
        GameObject monster = Instantiate(monsterPrefab);
        monster.transform.position = new Vector3(x, y, 0);

     
        int index = Random.Range(0, images.Length);//不会包含后一个数,会显示0到length-1
        //随机选择一个怪物
        SpriteRenderer renderer =monster.GetComponent<SpriteRenderer>();
        renderer.sprite = this.images[index];
    }
}


⑥实现效果

最近学校事情比较多,封校做项目和课设T T,不是每天都有时间总结学东西,但该写还是会写一点。

【做一个简单的飞机打怪兽小游戏②】进行优化

①使每个怪物大小一致

       //将头像的大小设置为100px(宽度)
        Sprite sprite = this.images[index];
        float imgWidth = images[index].rect.width;  //取得的图像实际宽度
        float scale = 100 / imgWidth; //缩放比例
        monster.transform.localScale = new Vector3(scale, scale, scale);

②怪物离开屏幕时销毁

   //判断当前怪物是否离开页面,离开则销毁
        Vector3 sp = Camera.main.WorldToScreenPoint(transform.position);
        if(sp.y < 0)
        {
    
    
            Destroy(this.gameObject);
        }     

③增加游戏背景并使其滚动
新建两个图片,放置一个节点下。

编写图片让图片向下移动,当一定位置时返回初始位置,使得图片无限循环的移动。(背景滚动效果)
bg_control.cs 挂载到游戏主控下

public class bg_control : MonoBehaviour
{
    
    
    Transform bg1;
    Transform bg2;

    public float speed = 1.0f;
    // Start is called before the first frame update
    void Start()
    {
    
    
        bg1 = GameObject.Find("/background/bg1").transform;
        bg2 = GameObject.Find("/background/bg2").transform;

        bg1.position = new Vector3(0, 0, 0);
        bg2.position = new Vector3(0, 10, 0);


    }

    // Update is called once per frame
    void Update()
    {
    
    
    //设定速度和移动
        float dy = speed * Time.deltaTime;
        bg1.Translate(0, -dy, 0);
        bg2.Translate(0, -dy, 0);

//当离开屏幕后滚动回到初始位置。
        if(bg1.position.y <= -10)
        {
    
    
            bg1.Translate(0, 20, 0);
        }
        if (bg2.position.y <= -10)
        {
    
    
            bg2.Translate(0, 20, 0);
        }

    }
}

【Unity脚本认识⑨】2022/01/01

1.添加音效
unity内的音频:支持2D/3D音效
3D:空间感,有近大远小的效果
支持格式:mp3/wav/ogg/aiff,可以在Inspector中试听
添加脚本时可把play onwake勾选掉

可以调用AudioSource的API进行播放
clip / mute / loop / volume / isPlaying
play() 播放 :正在播放时播放,就会停止当前播放,重新开始播放
Stop() 停止
Pause () 暂停
isplaying 判断是否在播放
PlayOneShot(clip) 新开一个播放

2.Invoke

Invoke 延时调用,过一会儿再调用

eg.Invoke(“SomeJob” , 3)
表示在3秒之后调用SomeJob方法

在每一帧Update后,系统都会检查待执行的Invoke,
连续调用多次Invoke之后,会添加多个延时调用
使用IsInvoking(name) 可以判定当前是否存在同名的延时调用

定时调用
InvokeRepeating(methodName, delay, interval):延时调用、并重复执行,实现了一个定时器的逻辑

3.消息调用
判断鼠标是否点击到相应物体

        //判断鼠标是否点中了当前物体
        if (Input.GetButtonDown(0))
        {
    
    
            //获取鼠标在屏幕上的位置
            Vector3 mousePos = Camera.main.ScreenToViewportPoint(Input.mousePosition);
            mousePos.z = 0;
            //两个向量相减并记录下减后向量的大小
            float distance = (mousePos - transform.position).magnitude;
            //判断向量大小是否在一个范围内
            if(distance < 2)
            {
    
    
                //打中了
            }
        }

将该main节点下面的所有脚本遍历一遍,寻找AddScore方法并调用它。
方法1:(简单,短)

GameObject main = GameObject.Find("游戏主控");
main.SendMessage("AddScore", 1);

方法2:

GameObject main = GameObject,Find("游戏主控")
MyGame myGame = main GetComponent<MyGame>();
myGame.AddScore();

SendMessage():查找目标方法并立即执行
1.遍历该对象上的所有MonoBehaviour组件
2.检查该组件上有没有xxx方法
3.若有此方法,则执行该方法
没有找到目标方法的时候,提示:SendMessae xxx has no receiver!

SendMessage()是同步调用,而不是异步调用。在目标对象上,查找目标方法并立即执行

4.交互界面UI

unity中的UI叫做UGUI
(1)添加Canvas(画布)
添加的同时会添加一个EvenSystem (事件系统)

猜你喜欢

转载自blog.csdn.net/weixin_42818410/article/details/121913770
今日推荐