DAY 一:
向量Vector
- 向量的长度
Vector2 :长度 = sqrt( x2 + y2 ) Vector3 :长度 = sqrt( x2 + y2 + z2 ) 可直接使用API求长度:
float len = v.magnitude;
- 向量的标准化Normalize
将向量的长度缩放成1,即单位向量 示例: (2,2) --> (0.707,0.707) 、(3,4) --> (0.6,0.8) 使用API进行标准化,示例:
Vector3 a = new Vector3(3f,4f,0);
Vector3 b = a.normalized;
Debug.log("标准化为: " + b.ToString("F3"));
- 几个常用的标准向量(静态向量)
Vector3.right;
Vector3.up;
Vector3.forward;
向量夹角
从a到b的夹角:示例
Vector3 a = new Vector3(2,2,0);
Vector3 b = new Vector3(-1,3,0);
float angle1 = Vector3.SignedAngle(a,b,Vector3.forward);
float angle2 = Vector3.Angle(a,b);
Debug.Log("a到b的夹角为:" + angle1);
物体的指向
示例:让一个飞机指向子弹,然后飞机向子弹移动
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);
}
void Update()
{
float step = 1.2f * Time.deltaTime;
transform.Translate(0,step,0,Space.Self);
}
屏幕坐标
游戏中的物体,有两个坐标: 空间坐标: transform.position 该物体在世界空间中的坐标 屏幕坐标 通过屏幕(摄像头)观察,该物体在屏幕上的位置 屏幕坐标系:
- y轴向上,x轴向右 ,左下角为(0,0);
- 宽度Screen.width(像素);高度Screen.height(像素)
获取一个物体的屏幕坐标:
Vector3 pos = transform.position;
Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
注:因为屏幕边界(左右)的不确定,所以去判断小物体是否移动至屏幕边界,得用屏幕坐标。
示例:让一架飞机左右飞行,不能超出屏幕边界
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class point : MonoBehaviour
{
private bool toRight = true;
void Start()
{
transform.eulerAngles = new Vector3(0,0,-90);
}
void Update()
{
Vector3 sp = Camera.main.WorldToScreenPoint(transform.position);
if( toRight && sp.x > Screen.width) {
toRight = false;
transform.eulerAngles = new Vector3(0,0,90);
}
if(!toRight && sp.x < 0) {
toRight = true;
transform.eulerAngles = new Vector3(0,0,-90);
}
float step = 1.8f * Time.deltaTime;
transform.Translate(0,step,0,Space.Self);
}
}
DAY 二
鼠标事件处理
探测鼠标事件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Mouse : MonoBehaviour
{
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
if (Input.GetMouseButtonDown(0)) {
Debug.Log("鼠标按下:" + Input.mousePosition);
}
if(Input.GetMouseButtonUp (0)) {
Debug.Log("鼠标抬起");
}
if (Input.GetMouseButton(0)) {
Debug.Log("鼠标正在被按下");
}
}
}
注:屏幕坐标转换为世界坐标时,记得把z坐标置0,放在2D平面上。
鼠标跟随事件
示例: 实现鼠标跟随效果,让飞机自动朝向鼠标飞行 思路: 首先判断鼠标的状态是否为按下,然后获取鼠标位置。 其次,让飞机移动到鼠标移动到的位置
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.UIElements;
public class point : MonoBehaviour
{
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
if (Input.GetMouseButton(0))
{
Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
pos.z = 0;
SetDirection(pos);
}
float step = 1.2f * 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);
}
}
鼠标拖拽
示例: 拖拽小飞机,移动它的位置 思路: 首先判断鼠标是否点击到飞机,即鼠标与飞机的位置之间的距离是否足够小, 其次,移动鼠标,将飞机的位置覆盖为鼠标的位置,让飞机移动到鼠标移动到的位置。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.UIElements;
public class MyJet : MonoBehaviour
{
private bool drag = false;
private Vector3 lastMousePos;
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
if ( Input.GetMouseButtonDown(0) )
{
Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
pos.z = 0;
float distance = (pos - transform.position).magnitude;
if(distance < 1.5f)
{
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 Functions
常见几个
- Awake() --脚本组件实例化时调用
- OnEnable() – 脚本组建启用时调用
- Start() – 脚本组件第一次执行之前
- Update()
- FixedUpdate()
(1)不以On开头的,由系统调用; Awake() 、 Start() 、Update()、FixedUpdate() (2)以On开头的,是相应事件的回调函数; OnEnable() 、OnDisable()、OnGUI() (3)当组件被禁用时,Start()不被调用,用于实例化的Awake()会被调用; (4)无论组件被重新调用几次,Start()只会调用一次; (5)一般情况下,可以把初始化写在Awake()或Start()中;
脚本的执行顺序
(1)对所有组件进行遍历,调用它们的 Awake() ,而后再依次遍历组件,调用 Start() ,再依次遍历组件执行Update() 。 (2)Exection Order : 脚本的执行顺序,即优先级。脚本的默认顺序都是0,无序。顺序值越小,则越优先。 **执行顺序的是设定: ** Project Settings --> Script Execution Order 或者 点击C#脚本 --> Execution Order (1)用下面两种方式中任一种打开Project Settings面板 Script Execution Order 项  (2)点击如下的红框 
(3)点击所要修改优先级的脚本
 (4)如下图所示,改变优先级。Dedault Time 是默认值,也可以直接拖拽脚本放在它上面【优先级增加】或下面【优先级减小】 
脚本的参数
脚本的参数,用于控制脚本的运行。 一个脚本是可以多次使用的,可以同时将脚本挂载在多个游戏对象上 示例:编写Fly.cs,让飞机按照设定的方向进行一定速度的移动。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fly : MonoBehaviour
{
public Vector3 speed ;
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
Vector3 step = speed * Time.deltaTime ;
transform.Translate(step,Space.Self);
}
}
如下图中,可以设定移动方向,以及移动速度 
引用类型的参数
引用类型: class类型的参数。 在脚本中,可以引用一个游戏对象、组件、或资源 示例:点击人物,实现换装功能。【在脚本中指定2张图片,便于动态的切换】 步骤:
- 在脚本中添加资源的引用 : public Sprite sprite0 ;
- 在Unity中的Inspector 中指定参数:把图片Sprite资源拖到Inspector 相应参数位置
- 在代码中切换图片显示。
注:也可以使用数组来表示多张图片: public Sprite[] sprites ;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeFace : MonoBehaviour
{
public Sprite sprite0;
public Sprite sprite1;
private int index = 0;
void Start()
{
Debug.Log("Start.....");
}
void Update()
{
if(Input.GetMouseButtonDown(0)) {
DoChange();
}
}
void DoChange() {
SpriteRenderer renderer = GetComponent<SpriteRenderer>();
if (index == 0) {
index = 1;
renderer.sprite = this.sprite1;
} else {
index = 0;
renderer.sprite = this.sprite0;
}
}
}
预制体
预制体Prefab:预先制作好的物体(模板),是一个资源文件.prefab。 将游戏对象事件制作好,作为资源备用,一般用于游戏对象的动态创建。
编辑预制体
打开方法: 1.在Project窗口,双击*.prefab资源; 2.在Inspector 窗口,点Open Prefab; 3.在Hierarchy 窗口,找到预制体的实例【图标为蓝色】,点右侧的小箭头。 注:
- 使用预制体,可以快速创建多个相同的实例;修改预制体时,每一个实例都会自动更新。
- 对预制体资源【实例】进行修改时,也可以改变预制体,使得其他预制体资源同步更新。
(1)随意选中某一预制体资源,Prefab工具,如下图:
- Open :打开Prefab编辑
- Select:定位Prefab资源,在Project面板中会定位到Prefab资源
- Overrides:点击:下拉小箭头:
- Revert All :取消对Instance所做的修改 - Apply All: 应用对Instance所做的修改 
断开Prefab 和 Prefab Instance 联系
在Hierarchy 窗口中,右键点击一个Prefab实例,执行 Unpack Prefab , 由它成为一个独立的游戏对象,不再和原始Prefab有联系。如下图: 
动态创建实例
在游戏的运行中使用代码来创建游戏对象。 示例: 每点击一次按钮,飞机就会发一枚子弹; MyJet 挂载至“飞机”上:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyJet : MonoBehaviour
{
public GameObject myPrefab;
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
if(Input.GetMouseButtonDown(0)) {
Fire();
}
}
void Fire() {
GameObject bullet = Instantiate(myPrefab);
bullet.transform.position = transform.position + new Vector3(0,1f,0);
bullet.name = "my bullet" ;
}
}
MyBullet 挂载在“子弹”上:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyBullet : MonoBehaviour
{
void Start()
{
}
void Update()
{
float step = 1.5f * Time.deltaTime;
transform.Translate(0,step,0,Space.Self);
}
}
实例的销毁
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);
}
}
注:要把文件存进资源里,然后拖到My Prefab。 否则,预制体会被销毁,也再也不能创建新的预制体实例。 
DAY 三
子弹发射(练习)
游戏对象:飞机,子弹(预制体)
要求:
- 定时发送子弹
- 通过键盘控制左右移动
定时器 :每0.4s发射一个子弹
- 定时发射子弹, 通过预制体不断地创建新的“子弹”实例。
- 子弹发射后,沿着y轴一直移动,超出屏幕边界后销毁。
键盘事件
Input.GetKeyDown(key)
Input.GetKeyUp(key)
Input.GetKey(key)
if(Input.GetKey(KeyCode.LeftArrow)) {
transform.Translate(-step,0,0);
}
附带有具体的实现代码见链接: https://blog.csdn.net/Mj_yong/article/details/119354146.
物理系统
物理系统Physics,即有物理规律作用的系统。 为物体添加刚体组件(RigidBody 2D)  添加刚体组件后,可以设置其质量、材质等属性,拥有质量后,会产生下落的效果。 区分: 2D游戏中是 Physics 2D 。在3D中,选择Physics.
静态刚体
分类:
- Dynamic:普通刚体,有质量、有速度
- Static: 静态刚体,质量无穷大、无速度 (适用于建筑物、地面等固定不动的物体)
- Kinematic:运动学刚体,无质量 (忽略物理规律的刚体,一般用于碰撞检测)

刚体的碰撞
添加碰撞体(Collider)组件后,才有碰撞的计算。  在规定碰撞范围后,两物体碰撞时会根据其范围与形状产生不同的效果。  注: 一般地,RigidBody 组件和 Collider 组件是同时使用的。
刚体的反弹
- 在Project窗口里,创建一个用于表示物体的材质
 创建后,在面板右侧出现下图  Friction: 摩擦系数 Bounciness :弹性系数,设为1. - 选择物体的 Rigidboby 2D组件
Material : 选择上述材质  在物理系统下,运动,碰撞,反弹都是有系统自动计算的。
DAY 四
延时调用
Invoke("methodName",IntervalTime);

消息调用
例子:若是得分,便调用方法AddScore(int value);
GameObject main = GameObject.Find("游戏主控");
MyGame myGame = main.GetComponent<MyGame>();
myGame.AddScore(1);
main.SendMessage("AddScore",1);
SendMessage():查找目标方法并立即执行;是同步调用。
- 遍历该对象上的所有c#脚本组件,检查该组件上有没有对应的方法;
- 若有此方法,则执行该方法;反正目标方法未找到,则提示SendMessage XXX has no receiver;
- 注:若没有找到,便检查自己是否单词拼写错误等。
交互界面UI
UI :User Interface用户交互界面。 Unity游戏中的UI,称为UGUI。 UI层不属于游戏空间,而是附在屏幕之上。
添加Canvas及其控件
- 所有的UI元素,都应该放在Canvas节点下面;
- 添加Canvas时,同时添加了EventSystem事件系统,它们是配合使用。
- 添加Canvas流程:
- 添加 Canvas
在 Hierarchy窗口,右键 GameObject —> UI —> Canvas 同时添加一个 Canvas ,和一个 EventSystem 事件系统管理器  - 指定 Canvas 的显式方式
选中 Canvas 节点, Render Mode : 设为 Screen Space – Camera Render Camera : 指向 Main Camera Plane Distance :显示平面与摄像机的距离 ,设为 5 .  - 添加 Text 文件控件
注意:所有 UGUI元素,都应该放在 Canvas 节点下面
右键选中 Canvas ,添加一个 Text 子节点 :  设置一下 Text 的属性, Font :字体样式,可以自己下载字体样式文件,然后拖至该框。 Best Fit :自动调整字体大小,适应 Rect矩形框 Color : 文本的颜色 
-
添加Image控件 和添加Text文件控件相同,在Canvas节点下,添加一个Image; 指定图片资源; 设置填充类型Image Type: Simple 拉伸, Sliced 九宫格, Tiled 平铺, Filed充满  注:Sliced ,九宫格方式填充,适用于带边框的图片(四个角不变,中央区域拉伸) 在添加图片资源之前需要对其进行九宫格切割: 选中图片,打开其Sprite Editor 面板,然后拖拉图片边框绿线节点,调整其位置如下图所示。  而后操作 Image Type设为Sliced。改变Image元素的矩形框大小,观察是否有畸形。 -
设置Button 可以设置按钮的背景图片、以及不同状态下按钮的颜色或高亮或变大【鼠标是否移动到按钮上】、设置按钮上的文本。
UI事件处理
UI事件一般处理的是鼠标点击事件。 (1)在“游戏主控”下挂载脚本,添加好一个Button,在Button组件下的On Click 点击 “ + ” ,然后将游戏主控拖至此处,继续选择游戏主控下的MyGame的Login() 。这样就指定了事件响应的处理。 
(2)也可以采用自定义Button的方式,需要自己编写脚本,实现IPointerDownHandler接口。
UI界面布局
(1) Rect Transform【矩形变换组件】的定位: 默认是居中定位。改变Game窗口大小,Text与Canvas中心的距离是不变的 Pos X/ Y. Width/Height的单位是像素。
如下图,点击center下的方框,可以选择改变Canvas控件的布局。 选择了某种布局后,无论Game窗口的大小和比例多少,它总是会在那个方位。  (2)UI布局Anchors  (3) UI布局Panel 实质上就是一个Image控件,但是自带一个背景图片。可用于界面布局,和其他控件形成一个父子关系。
|