#《shooting game》Unity小游戏 学习笔记
-
scene
场景视图:主要吧Hierarchy视图中的模型进行设计,摆放的区域
游戏视图:展示效果的区域
层次视图:Hierarchy主要存放游戏场景中具体使用的项目对象,比如摄像机,贴图等
项目视图:主要放所有的资源文件。比如脚本,预设,材质,动画
检查视图:Inspector可以理解为对项目中所有空间属性修改,设置的地方
-
Materials
Unity中材质Material:指色彩Main Color,纹理Texture,着色器shader(光滑度,透明度,反射率,折射率,发光度)等。因为预览方式是个球体,有时又被称作材质球。
Unity中赋予材质Material:将材质赋予给游戏物体Gameobject的Mesh Renderer组件,操作为直接从项目面板拖动到Element 0上。一种快捷方式是直接把材质拖拽到Scene场景面板或者Hierarchy面板上的游戏物体上。
-
Shader
Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出。绘图单元可以依据这个输出来将图像绘制到屏幕上。输入的贴图或者颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容(Shader及输入参数)打包存储在一起,得到的就是一个Material(材质)。之后,我们便可以将材质赋予合适的renderer(渲染器)来进行渲染(输出)了。
注:Shader不能直接拖拽到游戏物体上,需要我们先新建一个Material,然后把Shader赋予给Material,再把Material赋予给游戏物体。有些Shader还需要把对应的C#脚本赋予给游戏物体。
-
Animation
-
Prefab
可以理解为是一个游戏对象及其组件的集合,目的是使游戏对象及资源能够被重复使用。相同的对象可以通过一个预设体来创建,此过程可理解为实例化。
存储在项目文件中(Project视图)的状态时,预设体作为一个资源,可应用在一个项目中的不同场景中。当拖动预设体到场景中就会创建一个实例。该实例与其原始预设体的有关联的,对预设体进行更改,实例会同步修改。Prefab可以提升资源的利用率和开发的效率。
-
AudioSource
AudioSource 是 Unity 中的 Audio 组件,其主要用来播放游戏场景中的 AudioClip,AudioClip 就是导入到 Unity 中的声音文件。Unity 可导入的音频文件格式有 .aif,.wav,.mp3和.ogg。此外,Audio Source 还可以设置一些播放声音的效果,增强游戏场景中的声音效果。
AudioListener 是游戏中的声音接收器,一般位于 Main Camera 游戏对象上,它可以接收游戏中的所有音乐和音效(只要其所附加的游戏物体在音效的影响范围内),此外,每一个 Scene 中仅有一个 Audio Listener。
添加路径:首先选中 AudioSourceObj 游戏对象,然后在 Inspector 面板下方点击 Add Component -> Audio -> Audio Source。
-
Collider(碰撞器与Tirgger(触发器))
Box Collider是一个立方体外形的基本碰撞体,该碰撞体可以调整为不同大小的长方体,可以做门,墙等
-
Rigidbody(刚体)
刚体能让你的游戏对象被物理引擎所控制,它能通过受到推力和扭力来实现真实的物理表现效果。所有游戏对象必须包含刚体组件来实现重力、通过脚本施加力、或者与其他对象进行交互,这一切都通过NVIDIA的PhysX物理引擎来实现。
碰撞器(Collider)不需要刚体(Rigidbody)
刚体(Rigidbody)要发生碰撞,一定需要碰撞器(Collider)
碰撞器决定了碰撞发生时的边界条件
刚体决定了碰撞发生后的物体的运动效果
没有碰撞器的刚体,会在物理模拟中相互穿透。 -
tag
Tag 标签,就是一个标签。标签可以起到标识,区分的作用。
同一类的模型,我们可以根据需要给他们设置成统一的标签。可以选中一个模型,在模型的Inspector 面板上的顶部位置,设置Tag 选项为一个
具体的标签。
如果说引擎提供的标签没有自己想要的标签,可以自己手动添加新标签。
代码细节
一.移动
-
Palyer移动
float movev = 0;//纵向 float moveh = 0;//水平移动距离 //上下左右 if (Input.GetKey(KeyCode.UpArrow)) movev -= m_speed * Time.deltaTime; if (Input.GetKey(KeyCode.DownArrow)) movev += m_speed * Time.deltaTime; if (Input.GetKey(KeyCode.LeftArrow)) moveh += m_speed * Time.deltaTime; if (Input.GetKey(KeyCode.RightArrow)) moveh -= m_speed * Time.deltaTime; //移动 this.m_transform.Translate(new Vector3(moveh, 0, movev));
Input是一个包含了输入功能的类,包括几乎所有的键盘鼠标或触控操作函数。
Time,deltaTime 表示每帧的经过时间,那些需要每帧做增减变动的数值都需要乘于它。
this.transform 调用的是游戏体的Transform组件,Transform组件提供的主要功能都是和移动,旋转,缩放游戏体有关的。其中Vector3类型,用来表示x,y,z三个方向上的移动距离。
-
Enemy移动
protected virtual void UpdatedMove() { float rx = Mathf.Sin(Time.time) * Time.deltaTime;//左右移动 m_transform.Translate(new Vector3(rx, 0, -m_speed * Time.deltaTime)); }
UpdateMove 函数用来执行敌人的移动,使用了Sin函数使数值在-1~1之间循环变化,敌人就会左右迂回移动。
二.生成
-
发射子弹
m_rocketRate -= Time.deltaTime; if (m_rocketRate <= 0) { m_rocketRate = 0.1f; if (Input.GetKey(KeyCode.Space) || Input.GetMouseButton(0)) { Instantiate(m_rocket, m_transform.position, m_transform.rotation); //射击音效 m_audio.PlayOneShot(m_shoopClip); } }
Instantiate函数实例化是将original对象的所有子物体和子组件完全复制,成为一个新的对象。这个新的对象拥有与源对象完全一样的东西,包括坐标值等。
original:用来做复制操作的对像物体,源对象
position:实例化的对象的位置坐标
rotation:实例化的对象的旋转坐标(旋转四元数)
parent:实例化对象的父对象,就是给这个实例化对象找的爹,在完成实例化函数处理后,实例化对象将在父对象下,是父对象的子对象
instantiateWorldSpace(老的叫WorldSpaceStays):这个值为TRUE,表示实例化对象相对于世界坐标系的位置(是位置,不是坐标值,比如实例化前在Canvas左上角,实例化后还在左上角)不变,相对于父对象的坐标值变了。为false,表示实例化对象相对于父对象的坐标值不变,但在世界坐标系中的位置变了。PlayOneShot()在AudioSource 声源(即声音发出的来源)处播放 2D 音效
-
enemy子弹
void Awake() { GameObject obj = GameObject.FindGameObjectWithTag("Player"); if (obj != null) m_player = obj.transform; }
m_fireTimer -= Time.deltaTime; if(m_fireTimer<=0) { m_fireTimer = 2; if(m_player!=null) { Vector3 relativePos = m_transform.position - m_player.position; Instantiate(m_rocket, m_transform.position, Quaternion.LookRotation(relativePos)); } }
Awake方法继承于MonoBehaviour,它会先于start方法在游戏体实例化时执行一次。这里是使用FindGameObjectWithTag函数获得主角的游戏体实例。
Quaternion.LookRotation使子弹在初始化时朝向主角方向。
三.触发
-
enemy碰撞
void OnTriggerEnter(Collider other) { if (other.tag.CompareTo("PlayerRocket") == 0) { Rocket rocket = other.GetComponent<Rocket>(); if (rocket != null) { m_life -= rocket.m_power; if (m_life <= 0) Destroy(this.gameObject); } } else if (other.tag.CompareTo("Player") == 0) { m_life = 0; Destroy(this.gameObject); } if (other.tag.CompareTo("bound") == 0) { m_life = 0; Destroy(this.gameObject); } }
OnTriggerEnter函数,派生于MonoBehaviour,在碰撞体相互接触时触发
other.tag.CompareTo(“PlayerRocket”) == 0 语句用来比较字符串,判断遇到的碰撞体是否是PlayerRocket。
Rocket rocket = other.GetComponent()语句获得了对方碰撞体的Rocket脚本组件。
other.tag.CompareTo(“Player”) == 0语句用来判断遇到的碰撞体是否是Palyer,如果是则直接销毁自身。
-
声音与特效
public AudioClip m_shoopClip;//开火声音 protected AudioSource m_audio;//声音源 public Transform m_explosionFX;//爆炸特效
Instantiate(m_explosionFX, m_transform.position, Quaternion.identity);
m_audio.PlayOneShot(m_shoopClip);
优化
-
this.transform重复调用
protected Transform m_transform; void Start() m_transform = this.transform;
Update函数中每帧都调用this.transform组件,会造成效率问题。可以在对象初始化时只调用一次并且保存起来。Start 函数会在对象被实例化时自动调用一次,类似构造函数。
-
销毁
void Start() { m_transform = this.transform; Destroy(this.gameObject, m_liveTime); }
在子弹生成之初就定义何时销毁,降低游戏负荷
if (other.tag.CompareTo("bound") == 0)
{
m_life = 0;
Destroy(this.gameObject);
}
创建一个大于游戏场景的不可视的围墙tag为“bound”,敌人碰撞就会销毁