DIY Line v1.0 代码使用教程
转载请注明出处
作者:小唏丶
写在前面:
本教程旨在培养大家对于Unity/饭制关卡的兴趣,以及帮助大家对Unity(2017.4.2f2免费版)的使用形成初步的认识。
这个代码功能非常少,仅满足制作一个Dancing Line饭制关卡的最低需求,教程本身也有点填鸭式教育的感觉,一步一步跟着做就行了,非常简单……至于每一步为什么要这样做?这样做会怎么样?我应该会在每一步下面添加备注,因此本教程可能看起来会很冗长。
代码下载链接
本教程的步骤顺序和视频(油管:https://www.youtube.com/watch?v=57O6146uA2Q,B站:https://www.bilibili.com/video/av24232834)中的大不相同(因为录视频的时候是想到什么做什么,甚至漏了许多关键步骤)。
下面是The Winter冬日的项目工程的链接:https://pan.baidu.com/s/1ozyTFC7Pzy6jOhd0yqbVGw 密码:urxa
- Unity的基本操作
既然要用Unity来做东西,肯定要先学会Unity中的一些基本操作。但这并不是本教程的重点(因为百度上都有,这里就不在累述)。掌握在Scene面板中对于物体的移动,以及右键主观旋转,ALT+左键围绕旋转就可以了。
……
- 具体步骤
- 创建一个工程
- 双击Unity的图标,然后会有一个窗口
- 点击右上角的New
- 依次设置好项目名称、项目存放位置后点击Create Project
- 然后你会看到Unity的主界面,里面有各种面板(Hierarchy/Scene/Game/Project/Console/Inspector等),请先确定它们的位置,方便后续教程的阅读。
- 导入素材
什么是素材(asset)?素材就是你在游戏中要使用的东西,比如音乐,模型,代码,甚至一些UI,着色器。一切你不能在Unity中做出来的东西都可以称之为素材。在本篇教程中,使用的代码(line.cs)与音乐(thewinter.mp3)就是素材。
导入素材的方式有两种:
- 找到项目工程的存放位置,打开Assets文件夹,然后把东西粘贴进去
或者
1)直接把东西粘贴在Project面板中的Assets文件夹下
- 创建游戏中的主要物体
在一个简单的关卡中,必须有的东西有:一个Cube(线本身)、一个Cube(线的尾巴)、一个Camera(视角)、一个Plane(地板)、一个Cube(墙的原型)、一个Cube(类似于动画的装饰)、皇冠、钻石等……
以下是创建物体的方法:
- 在Hierarchy面板中,空白处点击右键→选中要创建的物体(基本的几何体在3D Object中,Empty是一个什么都没有的物体)
或者
1)顶部菜单栏 GameObject→选中要创建的物体(基本的几何体在3D Object中,Empty是一个什么都没有的物体)
或者
*1)或者 如果你有模型的话,创建模型的方式如下:在Project面板中找到导入的模型,按住,拖到场景中
阅读完这部分,请依次创建一个Cube(线本身)、一个Cube(线的尾巴)、、一个Plane(地板)、一个Cube(墙的原型)、一个Cube(类似于动画的装饰)、皇冠(用Empty代替)、钻石(用Empty代替)。
- 给物体插入组件
什么是组件(component)?一个游戏物体往往具备很多性质,比如线,它是实体的,有碰撞体积的,那就需要用到Rigidbody(刚体组件);又比如皇冠吃到后会有光晕,那就要用到Halo组件(本教程中用light组件代替)……等等
在本教程中,用到的组件有:
- Transform。物体自带,描述物体的位置、旋转、大小。
- Mesh Filter。物体自带,描述物体显示出的形状。
- Box Coilder。物体自带,描述物体碰撞(摸得到)的形状与大小。
- Mesh Renderer。物体自带,描述物体显示出的样子。
- Rigidbody。在Physics类目下,描述物体一些物理性质。
- Light。在Rendering类目下,描述物体发出光源的性质。
- Audio Sourse。在Audio类目下,描述物体携带的声音。
- Scripts。代码,本教程会用带cs。
以下是创建组件的方法:
- 在Hierarchy或者Scene面板中,选中被插入组件的物体。此时在Inspector面板中会显示物体的信息。
- 在Inspector面板中,点击Add Component,然后选中相应的组件。
或者
- 在Hierarchy或者Scene面板中,选中被插入组件的物体。此时在Inspector面板中会显示物体的信息。
- 顶部菜单栏 Component,然后选中相应的组件。
或者 对于代码来说
1)在Hierarchy或者Scene面板中,选中被插入组件的物体。此时在Inspector面板中会显示物体的信息。
2)在Project面板中,找到代码,直接拖到物体的Inspector面板上。
- 添加一些物体材质
什么是材质(material)?材质就是物体的材料、质感,一般设置在Mesh Renderer组件下。材质多种多样,透明的,描边的,荧光的都可以弄,但在本文仅使用标准材质。
以下是创建并设置材质的方法:
- 找到project面板,在Assets文件夹下,右键→create→material,并给该材质命名。
- 在project面板点击相应的材质,此时在inspector面板上会显示信息。
- 在inspector面板中,找到Albedo,点击右边的色块,设置颜色。
- 其他的选项你们可以自己动一动,观察有什么变化,网上都有具体说明,这里不累述
- 设置物体的信息
- 一个Cube(线本身)
①在视频中,我命名这个Cube为line
②添加Rigidbody组件,并去掉Use Gravity的勾(因为本代码不支持掉落与旋转)
③添加Audio Resource 组件,并点击AudioClip右边的小圆点,然后选择游戏音乐(设置一些东西的方式,一般就是点该项最后的小圆点,具体操作看视频)。并取消掉Audio Resource前的勾。(为什么取消?因为音乐不是一开始就播放,播放代码已经整理在line.cs中)
④添加line.cs的script组件,并依次设置Dline(线头),Tail(线的尾巴),Mcamera(摄像机),Road(墙),Decorates(装饰),Dieeff(死亡特效)。设置方法为点击右边的小圆点,然后找到对应物体。如果没有对应的物体(比如正文没有说到的Dieeff),请额外创建一个Empty并设置给它。
⑤找到Mesh Renderer组件下的Materials,左边有个三角形,点一下三角形能看到,Materials的具体信息,在Element 0 后面选择对应的材质。
⑥调整物体的大小位置(可以更改transform里的数据来微调,也可以直接在Scene面板中进行操作)
- 一个Cube(线的尾巴)
①在视频中,我命名这个Cube为tail
②禁用Box Collider组件(取消Box Collider前面的勾)。为什么?因为线的尾巴不应该有实体,否则会与线头产生物理碰撞。
③找到Mesh Renderer组件下的Materials,左边有个三角形,点一下三角形能看到,Materials的具体信息,在Element 0 后面选择对应的材质。
④调整物体的大小位置(藏起来,丢到最下面或者最上面就行,不要让玩家看到)
- 一个Camera(视角)
相机应该是工程创建出来后自带的。
①在视频中,我命名这个Main Camera为camera
②添加Audio Resource 组件,并点击AudioClip右边的小圆点,然后选择死亡音效,并取消掉Audio Resource前的勾。
③调整物体的位置。(小技巧:对于摄像机来说,可以先在Scene面板中调整好视角,然后在Hierarchy面板中选中camera这个物体,然后再依次点击顶部菜单栏GameObject→Align With View。这样就能调整摄像机的视角到目前编辑状态中Scene的视角。)
- 一个Plane(地板)
①找到Mesh Renderer组件下的Materials,左边有个三角形,点一下三角形能看到,Materials的具体信息,在Element 0 后面选择对应的材质。
②调整物体的大小位置(大小调大一点最好)
- 一个Cube(墙的原型)
①在视频中,我命名这个Cube为road(不要吐槽为什么不是wall,当时脑子抽了)
②设置材质(和前面一样)
③调整物体的大小位置(藏起来,丢到最下面或者最上面就行,不要让玩家看到)
④给它添加一个名为”wall”的标签(tag)。方法:在hierarchy面板中选中该物体,再在Inspector面板中找到Tag(Transform的上面),点击tag右边的选择框→Add Tag。然后跳转到Tags & Layers界面,点击Tags下面的加号,输入wall。然后再回到④开始的时候,给物体选上”wall”的Tag。
- 一个Cube(类似于动画的装饰)
如果你觉得这个装饰没有必要,请创建一个Empty代替创建一个Cube,然后做下列的①③④步。
①在视频中,我命名这个Cube为decorates
②设置材质
③调整物体的大小位置(藏起来,丢到最上面,不要丢到最下面)
④添加一个名为”deco”的标签(这步可能可以不用做,因为我代码好像改过了)
- light(光)
光源应该是工程创建出来后自带的。
①设置Transform下的rotation属性(它的位置改变与否并没有什么实际影响)即光照方向
②设置Shadow Type为Hard Shadows(其实不动也行,但是跳舞的线官方用的是这个。也可以设置成No Shadows,这样降低配置要求)
③其他数据你们也可以调整一下,自行探索
- 皇冠
不管你用的皇冠是什么样子的(哪怕你用Empty来代替皇冠),请执行下面的步骤
①添加名为”crown”的标签(必须设置,不然会报错……代码偷懒所以这么写了= =)
②添加light组件,并勾选draw halo后面的勾,这样就有光晕了,可以通过设置物体大小和光强来调整光晕的大小;然后禁用light组件(把Light前面的勾去掉)为什么禁用?因为只有吃到皇冠的时候才有光晕出现……
- 钻石
不管你用的钻石是什么样子的(哪怕你用Empty来代替皇冠),请执行下面的步骤
①添加名为”dia”的标签(必须设置,不然会报错……代码偷懒所以这么写了= =)
- 创建路径
相信目前,关卡的基本应该都设置好了。
- 找到线头(line)物体的line(Script)组件,勾选Road Maker
- 点击Unity窗口中的播放键(就是屏幕中间那个三角形,可以看看视频中我是怎么操作的)
- 开始游戏,根据音乐点击,使得线转弯。(这时是没有尾巴的,如果有尾巴,请看看Roadmaker有没有勾上;而且会生成墙壁)
- 点击完后,点击暂停按钮(一定不要结束!不然墙就都没了)。这时在hierarchy面板中会出现很多road(Clone)。选中它们,复制下来。
- 点击停止按钮(其实就是播放按钮那个三角形)。在hierarchy面板中,粘贴它们。
- 找到线头(line)物体的line(Script)组件,取消勾选Road Maker
截止到目前为止,你的关卡已经成型,接下来请自行装饰,更多特技请看一下狗带_die的Unity模板和nullptr的UE4模板!以后有空的话,我也许还会写关于Unity中更多功能的使用以及给出的代码怎么自定义更改吧
- 保存场景
别问我为什么现在才想到保存= =
1.Ctrl+S,把这个场景存在Assets文件夹里面,命名为main.unity(必须是这个,因为代码里面写了main = =)
- 关卡的打包
- Windows平台
- 基本设置
顶部菜单栏依次点击Edit – Project Settings – Player,你会看到有各种选项,Product name填写游戏名称,Default Icon是游戏图标,游戏图标要放在Assets文件夹里面的,其它选项请自行探索。
- 打包
顶部菜单栏依次点击File – Build Settings,选择Windows平台,再设置一下参数(其实不用动也行),点击build。然后会让你选择输出文件夹,命名……都设置到了以后就等待吧……打包时间视游戏工程和电脑配置决定……如果打包失败,请关注Console面板中的信息,复制过去百度一下,或者在评论区留言(然后我来百度2333)来进行解决。
- Android 平台
1)基本设置
顶部菜单栏依次点击Edit – Project Settings – Player,你会看到有各种选项,Product name填写游戏名称,Default Icon是游戏图标,游戏图标要放在Assets文件夹里面的,其它选项请自行探索。
然后点击下面的安卓图标,在Other Settings中的Identification里,Package Name要修改一下,改成什么都无所谓。
2)环境配置
这个有点麻烦,要装java sdk 这些,网上也有教程,这里就不写了吧……这块是最麻烦的一块,当时弄了半天才搞明白……= =
- 打包
和Windows平台同样的操作
- 调试
毕竟是在电脑上打包的,所以不保证弄出来的apk能在手机上很好地运行,可能会有打不开、闪退等情况;如果这个情况很不幸地发生了……那很不幸,我也不会弄……估计是环境没有配置好的问题。
如果帧数很低,那请尽量减少场景中的物体,以及取消阴影!
- 附件(请把文件命名为cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class line : MonoBehaviour {
//DIY Line v1.0 – script by Pegasi
//Please attach me to the line, then set dline,tail,mcamera,road,decorates,dieeff.
//You can add more to make this better!
public GameObject dline,tail,mcamera,road,decorates,dieeff;
private GameObject tempgo,tempdia,tempcr;
public bool direction,alive,start,load,roadmaker;
public float cameraspeed,temprm;
public Vector3 offset,tempcrgo;
private GameObject[] dia,cr;
List<GameObject> go =new List<GameObject>();
void Start ()
{
start = false;
load = true;
alive = true;
//find diamonds and crowns
dia = GameObject.FindGameObjectsWithTag (“dia”);
cr = GameObject.FindGameObjectsWithTag (“crown”);
//
}
void Update ()
{
if (load == true)
{
//save the initial distance between camera and line
offset = mcamera.transform.position – dline.transform.position;
cameraspeed = 0.03f;
load = false;
//
}
//restart
if(Input.GetKeyDown(KeyCode.R)==true){
SceneManager.LoadScene (“main”);
}
//
if (start == false && alive == true)
{//start game
if (Input.GetMouseButtonDown(0) == true || Input.GetKeyDown (KeyCode.Space) == true)
{
start = true;
dline.GetComponent<AudioSource> ().enabled = true;
}
}//
if (start == true)
{//turn
if (Input.GetMouseButtonDown (0) == true || Input.GetKeyDown (KeyCode.Space) == true )
{
if (direction == true)
{
direction = false;
}
else
{
direction = true;
}
}
}//
//build road/wall more efficiently
if (roadmaker)
{
temprm += 1;
//why there is temprm?to reduce the ammount of roads
//why build one when click?to ensure there is road/wall when line turns(or it will look weird)
if (temprm % 6 == 1 || Input.GetMouseButtonDown(0) == true || Input.GetKeyDown (KeyCode.Space) == true)
{
GameObject.Instantiate (road, dline.transform.position + new Vector3 (3, 0, -3), dline.transform.rotation);
GameObject.Instantiate (road, dline.transform.position + new Vector3 (-3, 0, 3), dline.transform.rotation);
}// // //
}
else
{//when you play …I dont put the part in Update but if(roadmaker) because these actions will create new gameobject and interfere the copying of road
GameObject.Instantiate (tail, dline.transform.position, dline.transform.rotation);
if (Input.GetMouseButtonDown (0) == true || Input.GetKeyDown (KeyCode.Space) == true )
{
if (direction == true)
{
go.Add(GameObject.Instantiate (decorates, dline.transform.position + new Vector3 (8, -9, -5), dline.transform.rotation));
}
else
{
go.Add(GameObject.Instantiate (decorates, dline.transform.position + new Vector3 (-5, -9, 8), dline.transform.rotation));
}
}
}//
}
void FixedUpdate ()
{//smooth camera
mcamera.transform.position = Vector3.Lerp (mcamera.transform.position, offset + dline.transform.position, cameraspeed);
//
//some ‘animations’ .I didnt use animation component because I dont know how it works in Unity 🙁
foreach (GameObject tempgo in go)
{
tempgo.transform.position += new Vector3 (0, 0.2f, 0);
}
foreach (GameObject tempdia in dia)
{
tempdia.transform.localEulerAngles += new Vector3 (0, 2, 0);
if (Mathf.Abs (dline.transform.position.x – tempdia.transform.position.x) < 1 && Mathf.Abs (dline.transform.position.z – tempdia.transform.position.z) < 1 && tempdia.transform.localScale.z >0)
{
tempdia.transform.localScale -= new Vector3 (0.3f, 0.3f, 0.3f);
}
}
foreach (GameObject tempcr in cr)
{
tempcr.transform.localEulerAngles += new Vector3 (0, 2, 0);
if (Mathf.Abs (dline.transform.position.x – tempcr.transform.position.x) < 2 && Mathf.Abs (dline.transform.position.z – tempcr.transform.position.z) < 2 && tempcr.transform.localScale.z >0)
{
tempcr.transform.localScale -= new Vector3 (0.26f, 0.26f, 0.26f);
tempcrgo = tempcr.transform.position;
}
if (tempcr.transform.localScale.z <=0)
{
tempcrgo += new Vector3 (Random.Range (-2f, 2f), Random.Range (-1f, 2f), Random.Range (-2f, 2f));
tempcr.GetComponent<Light> ().enabled = true;
tempcr.transform.position = Vector3.Lerp (tempcr.transform.position, tempcrgo, 0.02f);
}
}
//
if (start == true && alive == true)
{//how the line move
if (direction == true)
{
dline.transform.position += new Vector3 (0.3f,0,0);
}
else
{
dline.transform.position += new Vector3 (0,0,0.3f);
}
}//
}
void OnCollisionEnter (Collision x)
{//when die…
if (x.collider.tag == “wall”)
{
alive = false;
mcamera.GetComponent<AudioSource> ().enabled = true;
dieeff.SetActive (true);
}//
//when complete the level…
if (x.collider.tag == “Finish”)
{
offset = offset + offset + offset + offset;
cameraspeed = 0.01f;
}//
}
}