float len = v.magnitude;
Vector3 a = new Vector3(3f,4f,0);
Vector3 b = a.normalized;
Debug.log("标准化为: " + b.ToString("F3"));
//F3表示在打印显示时保留3位小数;
//注:向量打印默认显示保留一位小数
Vector3.right; //Vector3(1,0,0)
Vector3.up; //Vector3(0,1,0)
Vector3.forward;//Vector3(0,0,1)
从a到b的夹角:示例
Vector3 a = new Vector3(2,2,0);
Vector3 b = new Vector3(-1,3,0);
float angle1 = Vector3.SignedAngle(a,b,Vector3.forward);//顺着z轴进行旋转,带符号的,负为逆时针,正为顺时针
float angle2 = Vector3.Angle(a,b);
Debug.Log("a到b的夹角为:" + angle1);
示例:让一个飞机指向子弹,然后飞机向子弹移动
void Start()
{
//本物体的现指向:Y轴
Vector3 face = this.transform.up;
//找到所要指向的物体
GameObject target = GameObject.Find("子弹");
//找到两个物体之间的方向:两物体的空间坐标相减
Vector3 direction = target.transform.position - this.transform.position;
//一个是Y轴标准向量,一个两物体之间的向量,得到旋转角度
float angle = Vector3.SignedAngle(face,direction,Vector3.forward);
//旋转
this.transform.Rotate(0,0,angle);
}
void Update()
{
float step = 1.2f * Time.deltaTime;
//机头指向沿自己的Y轴指向前进
transform.Translate(0,step,0,Space.Self);
}
游戏中的物体,有两个坐标:
空间坐标: transform.position
该物体在世界空间中的坐标
屏幕坐标
通过屏幕(摄像头)观察,该物体在屏幕上的位置
屏幕坐标系:
获取一个物体的屏幕坐标:
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; //用于判断是否到达屏幕边界
// Start is called before the first frame update
void Start()
{
//让机头顺时针旋转90向右
transform.eulerAngles = new Vector3(0,0,-90);
}
// Update is called once per frame
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;
//机头指向沿自己的Y轴指向前进
transform.Translate(0,step,0,Space.Self);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Mouse : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//该方法用来让游戏以指定的帧率执行
Application.targetFrameRate = 60;
}
// Update is called once per frame
void Update()
{
//Input.GetMouseButtonDown( )探测鼠标按下,其中,0 代表左键,1 为右键,2 为 中键(滚轮)
if (Input.GetMouseButtonDown(0)) {
Debug.Log("鼠标按下:" + Input.mousePosition);
//打印此时鼠标的位置(屏幕坐标)
}
//Input.GetMouseButtonUp ( )探测鼠标抬起
if(Input.GetMouseButtonUp (0)) {
Debug.Log("鼠标抬起");
}
//Input.GetMouseButton ( )探测鼠标的状态,若按下,返回true;
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
{
// Start is called before the first frame update
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; // 把Z坐标置0,放到2D平面上来
// 控制机头的指向
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;//飞机最终移动到的鼠标位置
// 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)
{
//如果鼠标点击的是飞机,则将飞机位置变为鼠标位置
drag = true;
lastMousePos = pos;
}
}
//鼠标抬起,将drag初始化
if( Input.GetMouseButtonUp(0))
{
drag = false;
}
// 检查 ‘当前鼠标左键是否按下’ ,此为状态检查
if ( drag )
{
Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
pos.z = 0; // 把Z坐标置0,放到2D平面上来
Vector3 delta = pos - lastMousePos;
transform.position += delta;
lastMousePos = pos;
}
}
}
常见几个
(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 ; //速度参数
// Start is called before the first frame update
void Start()
{
Application.targetFrameRate = 60;
}
// Update is called once per frame
void Update()
{
Vector3 step = speed * Time.deltaTime ; //标量乘法
transform.Translate(step,Space.Self);
}
}
引用类型: class类型的参数。
在脚本中,可以引用一个游戏对象、组件、或资源
示例:点击人物,实现换装功能。【在脚本中指定2张图片,便于动态的切换】
步骤:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeFace : MonoBehaviour
{
//新建两个Sprite,然后在Unity中指定游戏对象,给其赋值;
public Sprite sprite0;
public Sprite sprite1;
private int index = 0;
// Start is called before the first frame update
void Start()
{
Debug.Log("Start.....");
}
// Update is called once per frame
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工具,如下图:
在Hierarchy 窗口中,右键点击一个Prefab实例,执行 Unpack Prefab ,
由它成为一个独立的游戏对象,不再和原始Prefab有联系。如下图:
在游戏的运行中使用代码来创建游戏对象。
示例: 每点击一次按钮,飞机就会发一枚子弹;
MyJet 挂载至“飞机”上:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyJet : MonoBehaviour
{
public GameObject myPrefab;
// 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)) {
Fire();
}
}
void Fire() {
//创建了一个子弹的实例:Instantiate(myPrefab)根据一个prefab,来创建一个实例,返回值便是创建出的GameObject
GameObject bullet = Instantiate(myPrefab);
//创建的实例在Y轴上方的1f处向上移动
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
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
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。
否则,预制体会被销毁,也再也不能创建新的预制体实例。
游戏对象:飞机,子弹(预制体)
要求:
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.
分类:
添加碰撞体(Collider)组件后,才有碰撞的计算。
在规定碰撞范围后,两物体碰撞时会根据其范围与形状产生不同的效果。
注: 一般地,RigidBody 组件和 Collider 组件是同时使用的。
//延长一点时间(IntervalTime)后调用。
//在每一帧Update之后系统会检查待执行的Invoke;
//逻辑上也可以自己计时,然后执行
Invoke("methodName",IntervalTime);
//注:方法名是一个字符串。
例子:若是得分,便调用方法AddScore(int value);
//找到节点
GameObject main = GameObject.Find("游戏主控");
//第一种方法:
//调用方法
MyGame myGame = main.GetComponent<MyGame>();
myGame.AddScore(1);
//第二种方法:
main.SendMessage("AddScore",1);
SendMessage():查找目标方法并立即执行;是同步调用。
UI :User Interface用户交互界面。
Unity游戏中的UI,称为UGUI。
UI层不属于游戏空间,而是附在屏幕之上。
右键选中 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事件一般处理的是鼠标点击事件。
(1)在“游戏主控”下挂载脚本,添加好一个Button,在Button组件下的On Click 点击 “ + ” ,然后将游戏主控拖至此处,继续选择游戏主控下的MyGame的Login() 。这样就指定了事件响应的处理。
(2)也可以采用自定义Button的方式,需要自己编写脚本,实现IPointerDownHandler接口。
(1) Rect Transform【矩形变换组件】的定位:
默认是居中定位。改变Game窗口大小,Text与Canvas中心的距离是不变的
Pos X/ Y. Width/Height的单位是像素。
如下图,点击center下的方框,可以选择改变Canvas控件的布局。
选择了某种布局后,无论Game窗口的大小和比例多少,它总是会在那个方位。
(2)UI布局Anchors
(3) UI布局Panel
实质上就是一个Image控件,但是自带一个背景图片。可用于界面布局,和其他控件形成一个父子关系。