
Unity是一个游戏引擎,其进入门槛远非零(与同一个Game Maker Studio相比),在本文中,我将告诉您开始学习它时遇到的问题以及找到的解决方案。 我将以我的Android 2d益智游戏为例来描述这些时刻(我希望它将很快在Play Market上发布)。
我并不假装是真的,我也不敦促您重复自己的事,如果您知道最好的方法,我只会向您展示如何做到这一点,也许刚开始熟悉Unity的人将以更少的工作量创建自己的独立游戏开发杰作。
我是电厂设计工程师,但是我一直对编码感兴趣,并且我对某些编程语言很熟悉。 因此,我们同意在Unity上创建游戏:
- 您需要了解一些C#或JavaScript(至少是C形语法)。
下面将要写的所有内容都不是Unity教程,在没有我的情况下,其中足够多的内容已在网络上繁殖。 下面将收集在Unity上创建第一个项目时可能发生的困难时刻。
值得警告的是,提供的脚本省略了大多数游戏逻辑(代表“商业秘密”),但是它们作为示例的性能已得到验证。
问题一-方向
方向锁我遇到的第一个困难是,我没有适当注意优化屏幕方向的可视界面。 解决方案是最简单的-如果您不需要更改屏幕方向以进行游戏,则最好将其阻止。 无需过度的灵活性,您就可以编写独立游戏,而不是另一百万美元的项目。 如果游戏在“肖像”中看起来更好,为什么还要进行大量的条件转换和锚点变化呢? 您可以在此处锁定屏幕方向:
编辑>项目设置>播放器
不同的权限在选定方向上以不同分辨率测试视觉界面也很重要,并且在测试时,请不要忘记存在比例为4:3(或3:4)的设备,因此我们可以安全地添加768x1024(或1024x768)。
更好的定位要调整游戏对象的位置和比例,最好使用Rect变换。

问题二-通讯
我遇到了类似的问题,因为我是第一次使用Game Maker Studio认识游戏开发人员,该脚本是游戏对象的完整部分,并且可以立即完全访问该对象的所有组件。 Unity具有通用脚本,并且仅将它们的实例添加到对象中。 简单地说,该脚本不直接知道它当前在哪个对象上执行。 因此,在编写脚本时,必须考虑接口的初始化,以使用对象的组件或其他对象的组件。
我们训练猫在我的游戏中,有一个GameField对象,在舞台上只有一个实例,还有一个同名脚本。 该对象负责显示游戏分数并再现整个游戏声音,因此在我看来,它对于存储更为经济(通常,游戏只有三个音频源-一个背景音乐,两个其他音效)。 该脚本解决了存储游戏帐户,选择AudioClip播放声音以及某些游戏逻辑的问题。
让我们更详细地讨论声音,因为此示例轻松显示了脚本与对象组件的交互。

自然地,该对象应该具有GameField.cs脚本本身和AudioSource组件,在我的情况下是两个整体(稍后将清楚原因)。
如前所述,脚本“不知道”该对象具有AudioSource组件,因此我们声明并初始化接口(目前,我们认为只有一个AudioSource):
private AudioSource Sound; void Start(){ Sound = GetComponent<AudioSource> (); }
GetComponent方法<component_type>()将从对象返回指定类型的第一个组件。
除了AudioSource,您还需要几个AudioClip:
[Header ("Audio clips")] [SerializeField] private AudioClip OnStart; [SerializeField] private AudioClip OnEfScore; [SerializeField] private AudioClip OnHighScore; [SerializeField] private AudioClip OnMainTimer; [SerializeField] private AudioClip OnBubbMarker; [SerializeField] private AudioClip OnScoreUp;
在下文中,Inspector`a需要方括号中的命令,
此处有更多详细信息。

现在,Inspector中的脚本具有新的字段,我们可以在其中拖动必要的声音。
接下来,在采用AudioClip的脚本中创建SoundPlay方法:
public void PlaySound(AudioClip Clip = null){ Sound.clip = Clip; Sound.Play (); }
要在游戏中播放声音,我们在适当的时候使用剪辑调用此方法。
这种方法有一个很大的缺点,一次只能播放一种声音,但是在游戏过程中,除了不断播放背景音乐外,可能有必要播放两种或更多种声音。
为避免产生刺耳的声音,我建议避免同时播放4-5种以上声音(最好是2-3种)的可能性,我指的是游戏短时的一阶声音(跳跃,硬币,玩家射击等),对于背景噪音而言,最好创建自己的声音源在产生这种噪音的物体上发出声音(如果需要2d-3d声音),或者在一个物体上产生所有背景噪音(如果不需要“音量”)。
在我的游戏中,无需同时播放两个以上的AudioClip。 为了保证两种假设声音的回放,我向GameField对象添加了两个AudioSource。 为了确定脚本中的组件,我们使用方法
GetComponents<_>()
它从对象返回指定类型的所有组件的数组。
该代码将如下所示:
private AudioSource[] Sound;
大多数更改将影响PlaySound方法。 我看到此方法的两个版本:“通用”(针对对象中的任何数量的AudioSource)和“笨拙”(针对2-3 AudioSource,这不是最优雅的方法,但是占用资源较少)。
两个AudioSource的“笨拙”选项(我用过)
private void PlaySound(AudioClip Clip = null){ if (!Sound [0].isPlaying) { Sound [0].clip = Clip; Sound [0].Play (); } else { Sound [1].clip = Clip; Sound [1].Play (); } }
您可以扩展到三个或更多AudioSource,但是条件数量将吞噬所有性能节省。
“通用”选项
private void PlaySound(AudioClip Clip = null){ foreach (AudioSource _Sound in Sound) { if (!_Sound.isPlaying) { _Sound.clip = Clip; _Sound.Play (); break; } } }
访问外来组件在运动场上,有Fishka预制件的多个实例,例如游戏筹码。 它是这样构建的:
子对象负责绘制芯片的主体,其颜色以及其他可变元素。 父级在筹码周围绘制标记边框(活动筹码必须在游戏中突出显示)。 该脚本仅在父对象上。 因此,要管理子Sprite,父脚本需要指定这些Sprite。 我这样组织起来-在脚本中创建了用于访问SpriteRenderer子级的接口:
[Header ("Graphic objects")] public SpriteRenderer Marker; [SerializeField] private SpriteRenderer Base; [Space] [SerializeField] private SpriteRenderer Center_Red; [SerializeField] private SpriteRenderer Center_Green; [SerializeField] private SpriteRenderer Center_Blue;
现在,Inspector中的脚本具有其他字段:

通过将子级拖放到相应的字段中,我们可以在脚本中访问它们。
用法示例:
void OnMouseDown(){
呼叫别人的脚本除了操作外部组件外,您还可以访问第三方对象的脚本,并使用其公共变量,方法,子类。
我将以一个已经众所周知的GameField对象为例。
GameField脚本具有一个公共方法FishkiMarkerDisabled(),该方法需要从场上所有芯片上“删除”标记,并在单击芯片时用于设置标记的过程,因为只能有一个活动标记。
在Fishka.cs脚本中,SpriteRenderer标记是公共的,也就是说,可以从另一个脚本访问它。 为此,请在GameField.cs脚本中添加Fishka类的所有实例的接口的声明和初始化(创建脚本时,将在其中创建相同名称的类),类似于对多个AudioSource进行的操作:
private Fishka[] Fishki; void Start(){ Fishki = GameObject.FindObjectsOfType (typeof(Fishka)) as Fishka[]; } public void FishkiMarkerDisabled(){ foreach (Fishka _Fishka in Fishki) { _Fishka .Marker.enabled = false; } }
在Fishka.cs脚本中,添加GameField类实例的接口的声明和初始化,然后单击该对象,我们将调用该类的FishkiMarkerDisabled()方法:
private GameField gf; void Start(){ gf = GameObject.FindObjectOfType (typeof(GameField)) as GameField; } void OnMouseDown(){ gf.FishkiMarkerDisabled(); Marker.enabled = !Marker.enabled; }
因此,您可以在不同对象的脚本(或类)之间进行交互。
问题三-保持
帐户管理员在游戏中出现帐户之类的问题后,直接的问题就是在游戏过程中以及游戏外部的存储情况,我也想保留一个记录,以鼓励玩家超越它。
当整个游戏(菜单,游戏,帐户取款)构建在一个场景中时,我不会考虑选项,因为,首先,这不是构建第一个项目的最佳方法,其次,我认为初始加载场景应为。 因此,我们同意项目中有四个场景:
- 加载程序-初始化背景音乐对象的场景(稍后将进行更多说明),并从保存位置加载设置;
- 菜单-具有菜单的场景;
- 游戏-游戏场景;
- 得分-得分,记录,排行榜的场景。
注意:场景加载顺序在“文件”>“构建设置”中设置。游戏中累积的积分存储在GameField类的Score变量中。 要在进入比分场景时访问数据,请创建一个公共静态类ScoreHolder,在其中我们声明一个变量以存储值,并声明一个属性以获取并设置此变量的值(方法由
apocatastas监视 ):
using UnityEngine; public static class ScoreHolder{ private static int _Score = 0; public static int Score { get{ return _Score; } set{ _Score = value; } } }
不需要将公共静态类添加到任何对象,可以在任何脚本中的任何场景中立即使用它。
场景转换方法中GameField类中的示例用法得分:
using UnityEngine.SceneManagement; public class GameField : MonoBehaviour { private int Score = 0;
同样,您可以在游戏过程中将记录帐户添加到ScoreHolder,但在退出时不会将其保存。
设置守护者考虑根据游戏的状态是否有声音效果来保存布尔变量SoundEffectsMute的值的示例。
变量本身存储在公共静态类SettingsHolder中:
using UnityEngine; public static class SettingsHolder{ private static bool _SoundEffectsMute = false; public static bool SoundEffectsMute{ get{ return _SoundEffectsMute; } set{ _SoundEffectsMute = value; } } }
该类类似于ScoreHolder,您甚至可以将它们组合为一个类,但是我认为这是不好的举止。
从脚本中可以看到,默认情况下_SoundEffectsMute声明为false,因此每次游戏启动时,无论用户是否之前对其进行了更改,SettingsHolder.SoundEffectsMute都将返回false(使用菜单阶段的按钮对其进行了更改)。
保存变量对于Android应用程序而言,最理想的方法是使用PlayerPrefs.SetInt方法进行保存(有关更多详细信息,请参阅
官方文档 )。 在PlayerPrefs中有两个选项可以保留SettingsHolder.SoundEffectsMute的值,我们称它们为“简单”和“优雅”。
“简单”的方式(对我来说如此)是在上述按钮类的OnMouseDown()方法中。 存储的值在同一类中但在Start()方法中加载:
using UnityEngine; public class ButtonSoundMute : MonoBehaviour { void Start(){
我认为“优雅”的方法不是最正确的,因为 使代码的维护复杂化,但是其中包含一些内容,我忍不住共享它。 该方法的一个特点是,在不需要高性能的时候调用SettingsHolder.SoundEffectsMute属性的setter,并且可以使用PlayerPrefs(读取-写入文件)来加载(哦,恐怖)。 更改公共静态类SettingsHolder:
using UnityEngine; public static class SettingsHolder { private static bool _SoundEffectsMute = false; public static bool SoundEffectsMute{ get{ return _SoundEffectsMute; } set{ _SoundEffectsMute = value; if (_SoundEffectsMute) PlayerPrefs.SetInt ("SoundEffectsMute", 1); else PlayerPrefs.SetInt ("SoundEffectsMute", 0); } } }
ButtonSoundMute类的OnMouseDown方法将简化为:
void OnMouseDown(){ SettingsHolder.SoundEffectsMute = !SettingsHolder.SoundEffectsMute; }
不值得通过读取文件来加载吸气剂,因为它涉及到性能至关重要的过程-在GameField类的PlaySound()方法中:
private void PlaySound(AudioClip Clip = null){ if (!SettingsHolder.SoundEffectsMute) {
通过以上方式,您可以组织游戏中所有变量的存储。
第五题-一劳永逸
这种音乐将是永恒的每个人迟早都会面临这样的问题,我也不例外。 按照计划,即使在菜单场景中也开始播放背景音乐,如果未关闭背景音乐,则可以在场景中播放菜单,游戏和乐谱,而不会中断。 但是,如果将“正在播放”的背景音乐对象安装在菜单场景中,则当您进入游戏场景时,背景音乐将被销毁并且声音消失,并且,如果将相同的对象放在游戏场景中,则在过渡之后将首先播放音乐。 事实证明,解决方案是使用放置在其“脚本”对象具有脚本实例的类的Start()方法中的DontDestroyOnLoad(对象目标)方法。 为此,请创建DontDestroyThis.cs脚本:
using UnityEngine; public class DontDestroyThis: MonoBehaviour { void Start(){ DontDestroyOnLoad(this.gameObject); } }

为了使一切正常工作,“音乐”对象必须是根目录(与主摄像机位于相同的层次结构级别)。
为什么加载程序中有背景音乐屏幕截图显示,“音乐”对象不在菜单场景中,而是在加载程序场景中。 这是由于以下事实导致的:菜单场景可以被加载多次(在乐谱场景之后,过渡到菜单场景),并且每次加载时,都会创建另一个“音乐”对象,并且不会删除旧的对象。 可以按照
官方文档中的示例进行操作,但是我决定利用这样的事实,即加载程序场景仅保证加载一次。
在此上,我在Unity上开发自己的第一款游戏时遇到的关键问题已成功结束,该问题在上传到Play市场之前(我尚未注册开发者帐户)。
聚苯乙烯如果这些信息有用,那么您可以支持作者,他将最终注册一个Android开发人员帐户。