小伙伴关心的问题:垃圾分类玩游戏(一款扔垃圾的游戏),本文通过数据整理汇集了垃圾分类玩游戏(一款扔垃圾的游戏)相关信息,下面一起看看。

垃圾分类玩游戏(一款扔垃圾的游戏)  第1张

“你做的前10个游戏都是垃圾,所以赶紧做掉吧。” ——《游戏设计艺术》作者

转眼间,这已经是这个系列的第三款作品了。这个游戏距离上次发布已经过去了10天,由于其中出现了很多变故,包括莫名其妙的连续生病等,大概开发的时间只有5-7天的样子。这次我选择的游戏主题是《魔塔》3D重置版,相信只要你接触过4399等小游戏网站,《魔塔》一定占据着你童年的一席之地。它太有名了,(在当时的条件下)太有趣了,以至于涌现出了各种各样的版本,有什么西游篇、无限加血篇、V1.1篇……光是我本人玩过的《魔塔》衍生作品,就有不下10来种。

最经典的《魔塔》第一关场景。有没有唤起你的回忆?

在本次重置中,我主要还原了《魔塔》开局的经典剧情,相信也是很多人心中的经典桥段,也就是勇者拿着神圣剑、神圣盾一路杀上2楼,然后被魔王击败的故事。当然,为了构建的方便性,我也对其中的某些元素做了一部分修改。废话不多说,先上DEMO:

(没想到吧,魔王竟是咱们第一个小游戏的弱智马里奥!这就是我的伏笔啊JOJO!!!)

相信看完DEMO后大家也能明白,为什么这款作品相较之前两款的开发时间要长这么久了。它相较于前作,我认为可以真正称为一个半成品“游戏”。他需要让角色有流畅的动作,攻击的音效,界面要有UI来了解当前的角色信息,怪物被击打后会有动作反馈,场景元素同样需要提供反馈(钥匙开门),角色进入下一关等等等等……在实际制作中,随着开发的深入,我发现要做成一个完整的游戏需要进行的步骤实在是太多太多了。在这个游戏中,我并没有完成我想到的全部内容(比如开始菜单),但今天的重要知识点已经多到需要我列一个表格来防止我忘记提了。

在这次技术细节分享中如果要像上次一样按照我的制作顺序一步步下来那整个文章就会太长了,因此我会直接进入重点。今天的第一个重点是动画器(Animator)。动画器的功能是帮助我们设计角色的动作,例如在上面的DEMO中,勇者的动作有:警戒、冲刺,攻击,怪物的动作有:警戒、攻击、死亡。

首先我们需要弄明白动画器最基本的原理,也就是:在指定的条件下,给指定的角色播放一段预设好的动画。我们先来看后半句的内容,也就是如何预设动画?首先我们要搞清楚,在Unity中你并不需要给每个模型都独立设置动作,因为动作和模型是分开的。比如说,我预设好了一个跑步的动画,这段动画我可以套用给小明的模型,可以套用给小红的模型,也可以套用给外星人的模型,前提是这些模型都具有完成动画所需要的骨骼。而如何构建模型骨骼,那就是3D模型师的工作了,我们要做的就是下载模型使用就完事了。因此,对于预设动画这一块,我们只需要在asset store中下载想要的动画,或者在模型自带的动画中寻找有没有合适的即可。

这里右边即人物骨骼示意图,它会告诉你这个模型缺少哪些骨骼,可以完成什么动作

所以,我们游戏开发程序员的重点工作就是给动画设置条件。这个条件从原理上非常好理解,比如我们可以这样设置:当任务速度为0时,循环播放警戒(idle)的动画;速度大于0时,从警戒切换到跑步(run)动画;点击攻击键时,播放攻击(attack)动画。实际上,这个逻辑也是我实际用在游戏主角上的动画逻辑,也就是上图中间的部分。在构建这个部分时,只需要将动画像拖一个文件一样拖到动画器中,然后给它们设置出入的条件即可,就像拼积木一样。如果你之前玩过scratch编程,那你就很好理解这个过程。

接下来的问题是,怎么给动画设置出入条件?这里其实也非常好理解,首先我们需要设置参数(在上图的左上方),添加一个speed,然后分别给idle和run动画的双箭头设置:当speed>0.1时,从idle切换到run,当speed<0.1时,从run切换到idle。随后,我们在人物的脚本中通过获取角色的动画器组件,使用.SetFloat方法来设置好speed的值,这样角色就会按照我们的想法来表现出指定的动作了。

转移条件设置,在图片下方

以上即动画器的基本原理与基本操作方法。如果想要进一步了解,可以观看这个视频(也是我的启蒙视频)

【移动专题】20分钟学会使用3D模型和动画_哔哩哔哩_bilibili​www.bilibili.com/video/BV1nq4y1n7sa

然后我们要讲讲关于角色的控制器问题。在上一个游戏中我们使用的控制器还是非常初级的,而这一次我们要编写一个较为鲁棒的、完整的控制器。首先需要区分一个概念,控制角色的方位有两个方法:一个是直接改变角色的位置(比如按一秒W键就把角色的Z坐标加10),即基于transform的方法,另一种是通过物理规则来控制角色运动,即基于rigidbody的方法。在之前我们使用的都是transform的方法,但如果直接改变角色坐标会带来一个问题:我们会把角色走到墙里面去。因为我们的程序写的时候可不知道哪里有墙,它只负责改变角色坐标。当然,设置碰撞体貌似可以解决这个问题,但这样做的话程序就会变成:角色走进墙里——碰撞体进行物理检测——发现角色在墙里——把角色弹出来。虽然勉强也可以完成角色移动的目标,但是会导致角色在墙边走的时候不停前后抽搐(因为不停重复进墙-弹出来-进墙-弹出来 这个过程)。而如果我们使用物理规则来控制角色运动,那么角色就根本不会走进墙里。

关于刚体运动,可以参考这个视频:

【移动专题】实用的物理刚体角色控制器_哔哩哔哩_bilibili​www.bilibili.com/video/BV1B64y1q7BN

于是,在对之前的代码进行修改后,再加上动画器内容,完整的主角控制代码长这样:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class move : MonoBehaviour { // Start is called before the first frame update public float speed = 2; public float turnspeed = 10; Animator anime; Vector3 distance; Rigidbody rigid; public AudioSource music; float forward_amount; float turn_amount; float timer; public int gold_key; public int hp; public int attack; void Start() { anime = GetComponent<Animator>(); rigid = GetComponent<Rigidbody>(); music = GetComponent<AudioSource>(); timer = 0; gold_key = 0; hp = 10; attack = 1; } // Update is called once per frame void Update() { float x = Input.GetAxisRaw("Horizontal"); float z = Input.GetAxisRaw("Vertical"); distance = new Vector3(x, 0, z); /////////////////////////////transform移动与刚体矛盾,应优化 // transform.LookAt(transform.position + distance); // transform.position += distance * speed * Time.deltaTime; ////////////////////////////////////////////////////////// Vector3 localMove = transform.InverseTransformVector(distance); //转换世界坐标为局部坐标 forward_amount = localMove.z; //局部坐标z坐标永远向前,x坐标永远向右 turn_amount = Mathf.Atan2(localMove.x, localMove.z); //转动分量为x与z比值,x越大转的越快 /////////////////////////////////这段代码可以设置走路 // if (Input.GetButton("Fire3")) // { // forward_amount /= 2; // } ////////////////////////////////////////////////// if (Input.GetMouseButtonDown(0) && timer <= 0) { anime.Play("attack01_hu", 0, 0); //播放一次攻击动画 music.Play(); timer = 0.9f; //攻击时不能移动 } timer -= Time.deltaTime; if(timer > 0) { forward_amount = 0; } Update_Ani(); } private void FixedUpdate() //处理刚体移动最好用FixedUpdate,其为真实时间而非每帧渲染 { rigid.velocity = forward_amount * transform.forward * speed; rigid.MoveRotation(rigid.rotation * Quaternion.Euler(0, turn_amount * turnspeed, 0));//四元数转动 } void Update_Ani() { anime.SetFloat("speed", distance.magnitude); } }

在DEMO中,我们可以看到角色走到楼梯后进入了第二关,游戏场景就切换了。这是如何做到的呢?首先,我们当然要设置好不同游戏场景的设计,并将不同关卡保存至Unity的不同场景中。然后,在左上角的“文件-生成设置”中,我们可以将设计好的场景拖进来:

然后,在对应物体的脚本中使用SceneManager.LoadSceneAsync方法,就可以做到切换游戏场景的目的。注意!一定是在生成设置中生成了的场景,才能用于切换。

这里以我的楼梯的脚本作为代码的例子:

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class go_to_next_level : MonoBehaviour { // Start is called before the first frame update void Start() { Scene my_scene = SceneManager.GetActiveScene(); Debug.Log("Active Scene is " + my_scene.name + "."); } // Update is called once per frame void Update() { } private void OnCollisionEnter(Collision collision) { if(collision.collider.gameObject.tag == "Player") { AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("第二关"); Debug.Log("Go to next level..."); } } }

另外,由于场景切换后原场景的对象就消失了,而有些对象我们不希望它消失(例如BGM),只要在对应脚本start中使用DontDestroyOnLoad(this.gameObject)方法,就可以做到这点。

以上是本次项目中的主要技术问题。在本文的下半篇中,我会讲解其他游戏元素的脚本编写中遇到的一些技术难题,以及开放游戏DEMO与完整源代码的下载。那么,让我们下次见~

(主要是因为现在还在生病,写到这里已经快6000字,于是临时决定分上下篇,给老年人一个休息的机会。长篇大论实在是太费精力了。。。)

更多垃圾分类玩游戏(一款扔垃圾的游戏)相关信息请关注本站,本文仅仅做为展示!