系统环境
操作系统:Win10
脚本语言:C#
引擎学习
文件夹预设
模型----------models
特效----------prefabs
纹理----------Textures
(包含材质、贴图、纹理)
声音----------Audio
材质----------Materials
(必须附着在引擎内的实体上)
脚本----------TutorialInfo
完成----------complete
(游戏编写完成)
界面选项介绍
使用重力:Rigidbody
中有选项Use Gravity
。
碰撞体积更加精确:使用Mesh Collider
得到的碰撞体积更加精确,同时消耗的资源也更多。
不显示物理的模型:通过禁用Mesh Renderer
(网格渲染器),可以不显示物理的模型,以方便查看Mesh Collider
(网格碰撞器)的大小。
不使用物理引擎检测碰撞:不需使用Collider
来检测物理碰撞,只需勾选Is Trigger
,把它当做Trigger
(触发器)用来触发动作。
修改背景:可以通过修改Camera
组件的Clear Flags
(清除标识)属性来实现。
使物体不受灯光效果控制:在背景图片的Shader里选择Unlit——>Texture
,可以使物体不受灯光效果控制,并且与原图的效果一样。
序列化
何为序列化?
答:我们都知道对象是暂时保存在内存中的,不能用U盘拷走了,有时为了使用介质转移对象,并且把对象的状态保持下来,就需要把对象保存下来,这个过程就叫做序列化,通俗点,就是把人的魂(对象)收伏成一个石子(可传输的介质)。
何为反序列化?
答:就是再把介质中的东西还原成对象,把石子还原成人的魂的过程。
序列化的具体操作
注:在脚本中新建的类,Unity需要将它序列化之后,才能在Inspector
视窗中显示出来。将[System.Serializable]
放在类名前即可完成序列化。
[System.Serializable]
public class Boundary{
public float xMin, xMax, zMin, zMax;
}
向量
向量标准化
注:Unity中使向量标准化的方法vector.normalized
解释:
单位化向量,表示了一个方向,而把值限定在了范围之内。
如(10,5,3)就成了(1,0.5,0.3)。这样做的好处是格式化向量,将向量锁定在1,这样就可以进行比较。否则,(10,5,3)和(31,2,7)怎么比较?
单位化指的是,让X 平方 + Y平方 + Z平方
的值,正好等于1。
用途:
在Unity里一般会单位化一个方向向量,再用它乘一个速度数值,来作为一个GameObject
移动的真实速度
碰撞体和刚体
1.若要使物体运动,则需要添加Physics->Rigidbody
(刚体)组件。
2.若要判断物体是否接触,则需要添加(碰撞体)组件,需要双方都有(碰撞体)组件,并且其中一方带有刚体。
3.碰撞体内的 is Trigger
选项,如果勾选了该属性,那么该物体就是一个虚体,有形而无实,它不再受力的作用,其它对象可以穿过它,但是如果这时满足碰撞事件产生条件,那么该物体就会产生触发事件。
心得
心得-1
注:处理Rigidbody
的时候最好用FixedUpdate()
而不是Update()
。
原因:
因为Update
的执行受场景GameObject
的渲染的影响,三角形的数量越多,渲染所需要的时间也就越长。FixedUpate
的执行则不受这些影响。
心得-2
注:使用Tag是识别对象的常用方法。
if(other.tag == "Boundary")
then......
心得-3
1.旋转是绕x轴旋转。
2.生成不在属性面板中的物体只需在脚本中创建public
变量,引擎中将物体赋给脚本即可,这样当游戏运行时加载物体的同时会运行物体上的脚本。
3.当遇到物体需要跟随父物体位置但又不想显示物体时,可以建一个GameObject,取其位置即可。
4.控制人物移动速度最好用transfrom
,能不用刚体就不用,用了刚体会有力作用。
功能实现
自由移动
注:通过调节刚体速度移动飞船的核心代码。
float moveHorizontal = Input.GetAxis("Horizontal"); //获取用户按下方向键x轴状态 -1或1
float moveHorizontal = Input.GetAxis("Horizontal"); //获取用户按下方向键x轴状态 -1或1
float moveVertical = Input.GetAxis("Vertical");
Vector3 sudu = new Vector3(moveHorizontal , 0 , moveVertical);
Rigibody.velocity = sudu 10; //执行此指令 = 向量各坐标10 = 向着x或z轴的速度*10 = 飞船将会以10的速度移动
自由移动且并超出屏幕边界
/*"Horizontal"和"Vertical"是输入管理器内置的两个轴。*/
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
/*将获取到的值向量化,Vector3 是一个三维变量类型。*/
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
/*velocity属性代表的是刚体的速度向量。包含了移动的方向和速度,也就是矢量和大小。*/
rigidbody.velocity = movement;
/*在FixedUpdate()函数中可以添加一个速度标识,以控制飞船的移动速度。*/
/*作用域为public的变量,可以在Inspector视窗中编辑变量的值。*/
public float speed;
/*利用Mathf.Clamp()函数,可以控制飞船不超出屏幕边界区域。*/
/*限制value的值在min和max之间, 如果value小于min,返回min。 如果value大于max,返回max,否则返回value.*/
static function Clamp (value : float, min : float, max : float) : float
rigidbody.position = new Vector3(
Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax),0.0f,
Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
);
/*获取组件的方法由于比较耗时,所以如果你在update修改transform的话,一般可以先在Start中获取这个组件的引用,然后在update中使用引用去修改。*/
飞船倾斜
public float tilt; //倾斜率
rigidbody.rotation = Quaternion.Euler(0.0f, 0.0f, rigidbody.velocity.x * -tilt); //飞船速度越到,倾斜越大
Quaternion.Euler //欧拉角函数
/*返回一个旋转角度,绕z轴旋转z度,绕x轴旋转x度,绕y轴旋转y度(像这样的顺序)*/
static function Euler (x : float, y : float, z : float) : Quaternion
子弹及子弹移动
视觉效果及引擎前台设置
注:对于子弹来说,合适的shader
并不是diffuse
,而是Particles(粒子)——>Additive(添加的)
。这会让子弹有高热的视觉效果。
Additive:黑色部分不会显示在场景中,因为黑色的值为0。白色的值为255,会显示纯白色。其他的颜色则会叠加显示。
Mobile->Particles->Additive:很多时候即便它不是一个手机游戏,使用此着色器会使游戏运行时消耗的资源更少,但有时会牺牲着色的质量或部分控制功能。
子弹移动
注:要移动子弹的方法是移动它的Collider
。因此要添加Rigidbody
组件,以及移除自带的Mesh Collider
组件。
/*使得子弹向前移动*/
rigidbody.velocity = transform.forward;
注:
1.新建分组管理的对象后,要及时Reset
。
2.Quad
对象一定要添加刚体组件,才能移动。
3.记得将Use Gravity
前面的勾去掉。
4.将Rigidbody
和Capsule Collider
组件放在父级对象上,这样就可以将视觉效果和逻辑控制分开,子对象只要设置不同的材质就可以了。
克隆多个子弹
注:应使用Instantiate()
实例函数。
static function Instantiate (original : Object, position : Vector3, rotation : Quaternion) : Object
Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
键盘发射子弹
注:当按下按钮时发射子弹,需要用到Input.GetButton()
获取按钮函数。
static function GetButton (buttonName : string) : bool
Unity Input
——>输入控制管理器:Edit——>Project Setting——>input
子弹飞出场景自动销毁
注:要实现物体飞出场景后自动销毁的逻辑,可以用一个“盒子”将场景覆盖住,物体飞出盒子后就将它销毁。使用OnTriggerExit()
函数。
/*使用Destroy破坏函数来实现物体的销毁。*/
function OnTriggerExit (other : Collider) : void
/*Destroy()不会立即摧毁物体,而是把它们标记为将要被摧毁的对象。所有被标记的对象会在每帧结束时摧毁*/
Destroy (other.gameObject);
使球体旋转
1.对刚体的angularVelocity
(角速度)赋一个随机值,可以选用insideUnitSphere()
函数。
2.若要刚体始终旋转,而不会停止,应该将Angular Drag
(角阻力)的值设为0。
实现延时效果
方法一:
要实现延时效果,可以使用WaitForSeconds.WaitForSeconds(等待秒数)
函数。
方法二:
创建一个yield指令,yield return new WaitForSeconds(float time);
来等待给定的秒数。
注意:
update
或fixedupdate
中不可使用协程,因为update
会每帧运行一次协程方法,当协程方法内的延时完成时多个update运行的协程方法会同时执行。
/*关于在update中延时的方法*/
update(){
if(Time.time >= a){
a = Time+2;
}
}
在脚本中播放音频
audio.play();
加载场景
注:要重新加载场景,可以使用Application.LoadLevel(加载关卡)
函数。
在不同cpu下主角移动距离相同
playerSpeed * Time.deltaTime
通过射线检测使人物跟随鼠标转动
射线是什么:由一个点向一个方向无限射出直到碰到碰撞体后停止。
实现步骤:
1.给地板设置一个layer
(图层)。
2.获取地板的layer
层。
int floorMask = LayerMask.GetMask("floor");
3.让相机去获取鼠标的地图位置,把他转成一个射线点,有了这个点,就可以去控制主角的朝向。
Ray mouseRay = Camera.main.ScreenPointToRay (Input.mousePosition);
4.创建一条由相机发出的射线以100的速度穿过鼠标位置的射线点,这里第四个参数设置了地板的layers
层意指射线触碰地板停止无限延伸射线碰撞到地板的信息会通过floorHit
传回;当射线与地板碰撞时返回true
。
RaycastHit floorHit;
if (Physics.Raycast(mouseRay, out floorHit, 100f, floorMask))
5.得到人物与射线碰撞点间的距离;为了之后计算旋转量。
Vector3 playerRotate = floorHit.point - transform.position;
6.传入人物与射线碰撞点间的距离从而计算出人物要朝向射线碰撞点的旋转量。
/*就像你和目标拉一条线,那条线的长度高度知道的话,他才可以做出正确的注视旋转*/
Quaternion rotate = Quaternion.LookRotation(playerRotate);
7.传入四元数,进行实际物体旋转。
注:四元数大多数用于表示旋转,不过不要想成普通的翻转,而是每个点都可以翻转,用四元数,可以直接做到纸翻页的效果。
例如这是一个四元数,意思为绕y轴旋转30度:Quaternion.Euler(new Vector3(0, 30, 0));
playerRg.MoveRotation(rotate);
2d相机跟随主角
void Start()
{
/*计算出相机与主角之间的距离*/
offset = transform.position - target.position;
}
FixedUpdate()
{
/*时刻获取玩家当前位置并加上相机与玩家距离*/
Vector3 cameraPos = target.position + offset;
/*使用线性插值来保证相机平滑移动,相机当前位置,要移动的目标位q置,移动所花费时间,插值函数会自动计算使其达到平滑移动*/
transform.position = Vector3.Lerp(transform.position, cameraPos, Time.deltaTime * speed);
}
实现怪物简单跟随主角
/*使怪物朝向玩家*/
myTransform.rotation = Quaternion.Slerp(myTransform.rotation, Quaternion.LookRotation(targetPosition - myTransform.position),
rotationSpeed * Time.deltaTime);
//Slerp是一个旋转插值,输入旋转起始点和旋转目标点进行旋转;从怪物本身的方向旋转到朝向玩家的方向,这是一个有过程的旋转
//玩家位置-怪物位置=距离,由于他们是一个向量,所以包含了除坐标以外的其它信息,通过Quaternion.LookRotation计算出要到达这个距离的方向
/*计算玩家与怪物之间距离*/
maxDistance = Vector3.Distance(targetPosition, myTransform.position);
if (maxDistance >= 1.2f)
{
//当距离大于两米时移动
myTransform.position += myTransform.forward moveSpeed Time.deltaTime;//让怪物朝着自己的正面移动
}
else
{
//当距离小于两米时的动作
}
使用自动寻路实现怪物跟随
1.给角色添加Nav Mesh Agent
2.Window——Navigation-——选中所有障碍物
,勾选Navigation Static——bake
using UnityEngine.AI;
public Transform playerTs;
NavMeshAgent boosNav;
boosNav.SetDestination(playerTs.position);
动画播放到指定时间后执行代码
方式一:
选中动画——Windows——Animation
进入编辑器-可以看到动画的开头帧和结尾帧分别为Event
和RestartLevel
void RestartLevel() //动画最后一帧执行完后执行此方法
{
Destroy(this.gameObject);
}
方式二:
如果动画只读,则可以选中动画——edit——events
,然后点击+号-输入方法名,作用脚本即可,编辑删除鼠标移动到关键帧上右键即可。
人物移动播放脚步声
新建GameObjct——>添加声音组件——>gameobject放在人物下——>gameobject赋给jiaobuMuscic
public AudioSource jiaobuMuscic; //移动时播放脚步声
x = Input.GetAxisRaw("Horizontal");
z = Input.GetAxisRaw("Vertical");
/*播放脚步声*/
if ((z!=0 || x!=0) && (!jiaobuMuscic.isPlaying))
{
jiaobuMuscic.Play();
}
/*停止脚步声*/
if (z==0 && x==0)
{
jiaobuMuscic.Stop();
}
控制组件显示或隐藏
组件.enabled = true; //组件可见
控制物体显示或隐藏
思路:在场景中停用物体,需要注意的是必须在fixedupdate或update中调用该方法
gameobject.SetActive(true);
不同脚本中互相传参
方式一:
public
脚本名——>面板直接拖拽。
方式二:
当要调用另一脚本变量的脚本为prefab
时,此时无法拖拽。
1.通过FindGameObjectWithTag
获取另一脚本所在gameobject
。
2.通过gameobject
获取脚本。
3.通过脚本调用变量。
判断多个物体是否满足条件
说明:当要从多个动态生成的物体中有一个符合条件要求,要对其进行处理时,很难从很多物体中定位到该物体。
思路:直接在物体上写脚本,实时判断该物体是否满足情况即可。
实现随机名字
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
public class reName : MonoBehaviour {
public string chineseWords = "";
/// <summary>
/// 随机产生常用汉字
/// </summary>
/// <param name="count">要产生汉字的个数</param>
/// <returns>常用汉字</returns>
// Use this for initialization
void Start () {
System.Random rm = new System.Random();
Encoding gb = Encoding.GetEncoding("gb2312");
for (int i = 0; i < 3; i++)
{
// 获取区码(常用汉字的区码范围为16-55)
int regionCode = rm.Next(16, 56);
// 获取位码(位码范围为1-94 由于55区的90,91,92,93,94为空,故将其排除)
int positionCode;
if (regionCode == 55)
{
// 55区排除90,91,92,93,94
positionCode = rm.Next(1, 90);
}
else
{
positionCode = rm.Next(1, 95);
}
// 转换区位码为机内码
int regionCode_Machine = regionCode + 160;// 160即为十六进制的20H+80H=A0H
int positionCode_Machine = positionCode + 160;// 160即为十六进制的20H+80H=A0H
// 转换为汉字
byte[] bytes = new byte[] { (byte)regionCode_Machine, (byte)positionCode_Machine };
chineseWords += gb.GetString(bytes);
}
}
// Update is called once per frame
void Update () {
}
}
评论区