你好! 这是我从零开始学习unity的博客。
每有空就会添加一个小标题用于总结当天所学的知识。
在小标题下会用加粗字体一句话总结当日学习的知识点。
刚开始接触unity什么都不懂,有错误还请多多包涵( ゚Д゚)ノ
unity核心类图
官方用户手册
【翻译注释文档】
游戏蛮牛Unity 用户文档1.0
代码调试和配置环境
调试C#代码
本日安装了Unity 和 Unity Hub 。
学习到了:初步认识了unity界面、学习了什么叫场景,如何新建场景、场景编辑器、如何添加资源、什么叫游戏对象,坐标系。
1.unity界面初识
新建项目后安装unity界面如图所示。
Project项目面板:项目内的资源文件。图中下方的面板。可在里面直接建立场景、文件夹、对象、脚本等等物品
Scene场景视图:以可视化的形式展现游戏内容。
面板内部左上角一排工具分别为:抓取工具(可拖动视图)、旋转平移工具、自由变换工具和拉伸工具。其中旋转可进行xyz轴的旋转。
右上角工具为相机和将视图画面2D3D切换的可视化工具。
Game游戏视图:对游戏进行播放预览。
Hierachy层级面板:以树状形式显示游戏中的对象(左侧面板)
inspector 组件面板:显示一个游戏对象的全部组件
Console控制台:打印调试的
2.什么叫场景,如何新建
场景就是一个游戏里的关卡,或者某个画面。
我个人理解,以找茬游戏为例,应该是一个解密里的一个关卡,在该关卡中进行解密,到下一关就切换为下一个场景。
新建场景:在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的摄像机来理解)
今日学习:认识了游戏对象的概念及其操作和显示顺序、摄像机、组件。
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.组件
组件:用于实现一种功能。
在右侧面板种可以添加一些组件
今日学习:认识了什么叫脚本、如何挂载脚本、学习查看手册、认识了脚本中的Start()和Update()
什么是脚本
游戏对象的行为由附加的__组件__控制。脚本可以理解就是在写一个自用组件(?
怎么挂载脚本
Assets窗口创建一个C#脚本
双击脚本启动VS
在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()
今日学习:重新配置了环境、学习帧率的概念、如何获取当前对象和其他对象的组件
今早打开脚本不知道为什么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("相应对象的路径");
今日学习:父子节点关于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);
}
今日学习:向量的概念,向量标准化,向量算数,屏幕坐标的概念
⑦向量的概念,向量标准化,向量算数
求向量长度的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);
屏幕的边界(可以用屏幕坐标来控制游戏对象不超出屏幕的边界)
今日学习:获取鼠标事件,游戏对象跟随鼠标,鼠标拖拽功能的实现*
⑨获取鼠标事件,游戏对象跟随鼠标
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;//更新鼠标的新位置
}
}
今日学习:什么是事件函数,脚本参数,认识预制体
⑪事件函数(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中重要的类
//创建一个预制体资源音符
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里的刚体、碰撞检测的组件使用
键盘事件
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);
}
}
}
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 (事件系统)