幻蓝博客 – 孤月蓝风

追寻互联网科技、Unity开发、AR/VR开发、游戏开发、Web前后端开发等技术。

Unity中制作无限循环滚动的背景


在游戏中,经常会用到需要无限循环滚动的背景,例如打飞机游戏、跑酷游戏等。

本篇文章向你介绍两种简单的制作方法。

方法一:使用Material方式来制作。

优点:简单省内存。

图片导入到Unity中之后,Texture Type使用Texture。如果图片是透明的话,那么勾选Alpha Is Transparency,Wrap Mode更改为Repeat。

图片类型设置为Texture

之后创建一个Material,如果图片是透明的,则Shader选为Unlit/Transparent,如果不是的话,使用Mobile/Diffuse即可。

接下来,创建一个Quad,将材质球拖上去,然后再挂个脚本,脚本里的Update里加一句话即可:

MR.material.mainTextureOffset += new Vector2(Time.deltaTime * moveSpeed * 0.02f, 0);

方法二:两个Sprite进行无限循环。

优点:如果你在做2D游戏,而且需要使其他Sprite与背景保持同等速度会很方便。例如一个无限循环的陆地,上面会随机生成树、石头等,而树和石头需要与陆地保持同样的速度移动。

创建两个Sprite,一个默认position为0,另一个则是0加上图片的宽度,一般Pixel Per Unit为100,图片宽度为2048像素,则第二章图片的x为20.48。

两个Sprite都挂上一个Move脚本,脚本里的Update加几行代码即可:

transform.Translate(Vector3.left * Time.deltaTime * speed);
if (transform.position.x < -20.48f)
{
    transform.position += new Vector3(20.48f * 2, 0, 0);
}

两个方式各有优势,方式一只需要创建一张图片,而方式二需要两张,但是方式二在很常用,至于选择哪个,就看项目的具体需求了。

希望本分享对你有所帮助!


Unity定制Inspector常用的辅助功能


在Unity中,我们可以很方便的对Inspector进行定制,有很多简单方便的小功能都会用到。例如给一个int或float加一个范围等。这些操作不止可以在Editor类型的脚本中进行,在普通的MonoBehaviour中也可以做到。这里给大家分享一下在Unity中试验的结果。

AttributeTest.png

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

//在 Add Component 按钮中增加一个菜单项
[AddComponentMenu("Transform/Follow Transform")]

//在 Create 菜单中,增加一个菜单项,前提条件是:该脚本继承自ScriptableObject
[CreateAssetMenu(fileName = "New AttributeTest File", menuName = "AttributeTest", order =1)]

//限制同一个GameObject只能有一个该组件(脚本)
[DisallowMultipleComponent]

//编辑器的编辑模式下,在Update、OnGUI、 OnRenderObject时会执行
[ExecuteInEditMode]

//自定义组件右上角?图标的链接
[HelpURL("http://fengyu.name/")]

//如果该组件继承自 MonoBehaviour,则必须有一个 BoxCollider 组件同时存在
[RequireComponent(typeof(BoxCollider))]

//没发现任何改变
[SelectionBase]

//只有在该组件继承自 StateMachineBehaviour 时有效,具体作用未知
[SharedBetweenAnimators]

public class AttributeTest : MonoBehaviour
{
    //将一个字段变为颜色原则
    [ColorUsage(true)]
    public Color color;
    
    //脚本管理的地方增加一个菜单
    [ContextMenu("Do Something")]
    void DoSomething()
    {
        Debug.Log("Perform operation");
    }
    
    //字段名称处,增加一个右键菜单。第一个参数为菜单名称,第二个参数为功能的函数名
    [ContextMenuItem("Reset", "ResetBiography")]
    [Multiline(2)]
    public string playerBiography = "";
    void ResetBiography()
    {
        playerBiography = "";
    }
    
    //该值,只有在点击Enter键、丢失焦点时才会被返回
    [Delayed]
    public float delay;
    
    //没有发现产生的影响
    [GUITarget(0, 1)]
	void OnGUI()
	{
		GUI.Label(new Rect(10, 10, 300, 100), "Visible on TV and Wii U GamePad only");
	}
    
    //用于增加一个标题头
    [Header("Header之后的部分")]
    public string header;
    
    //会在 Inspector 中隐藏字段
    [HideInInspector]
    public string hide;
    
    //创建一个显示3行的文本框
    [Multiline(3)]
    public string multiline;
    
    //使值变成滑动条的方式,并限制大小
    [Range(0, 10)]
    public float range;
    
    //加载时初始化运行函数
    [RuntimeInitializeOnLoadMethod]
    static void OnRuntimeMethodLoad()
    {
        Debug.Log("After scene is loaded and game is running");
    }
    
    //可以序列化私有字段,让 private 也在 Inspector 中显示
    [SerializeField]
    private string serializeField;
    
    //创造一个高度为10的空白区域,可以用做分割线,高度单位估计是像素
    [Space(10)]
    public string space;
    
    //创建一个文本区域,文本区域会单独一行存在
    [TextArea]
    public string textArea;
    
    //当字段获得焦点后,鼠标指向字段,会获得的提示信息
    [TooltipAttribute("这是这个字段的提示信息")]
    public string toolTip;
}


Unity3D《拾荒者》学习笔记(一):单例模式


最开始看到Unity官方的《拾荒者》教程时,还是初学Unity,很多知识点都不知道,如今使用Unity工作也有半年了,重新再看教程,很多地方也都能看懂了。于是重新做一下笔记,温故而知新!

单例模式

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

以上的定义,来自维基百科。

单例模式在游戏开发中是很常用的一种设计模式,下面是在Unity中的一种典型案例:

假如我们的游戏有1-N个游戏关卡(Scene),我们需要一个管理器用来对分数进行管理,在Unity中创建一个名为GameManager的GameObject,在关卡1中,我们创建了这个GameObject,并对分数进行了存储,这时,我们成功过了第一关,需要切换到第二关,切换场景时,默认会销毁当前场景的全部GameObject,而我们需要保留分数信息。这种情况下,我们就需要单例模式来实现这个功能。

public static GameManager instance = null;
void Awake()
{
    if (instance == null)
    {
        instance = this;
    }
    else if (instance != this)
    {
        Destroy(gameObject);
    }
    DontDestroyOnLoad(gameObject);
}

上面的代码就是在Unity中单例模式的完整实现。

Static:

微软的解释是:使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员。从他人的博客中我看到这样一句解释:表示此方法为所在类或所在自定义类所有,而不是这个类的实例所有。你可以通过这两句话理解Static。

在Awake中,我们首先判断该静态变量是否为null,如果为null,则将当前的类赋值给静态变量。如果不为null且不是当前类,那么说明我们目前有多个GameManager存在,此时需要销毁其他的,只保留之前使用的。通过这种方法,我们就可以通过单例模式来实现分数的管理。当然,不要忘记加上DontDestroyOnLoad,这个是让实例不在场景切换时销毁。

以上就是我对单例模式的学习与理解,如果有不对的地方,欢迎指正!


Unity3D制作2D魔塔游戏一:准备工作


《魔塔》是一种数值类益智游戏,最早在文曲星上玩过,一直特别喜欢。在开始学用Unity3D做游戏后,考虑做一个RPG游戏,于是准备从魔塔这个游戏入手。之所以选魔塔,是因为魔塔比较简单,涉及的系统相对较少,为RPG做准备,一步一步来。

在最开始制作的时候,使用的是Unity4.6,随后升级到了Unity5。我在蛮牛上看到过制作魔塔的视频教程,制作方法跟我的不一样,个人觉得我的方法也是有很多可取之处的。这次开源的版本是第二版,已经足够稳定,没有发现残余BUG,只是有一些功能并没有加入。在制作的过程中,我使用到了许多插件,我造的轮子不一定有他们的好,又何必自己造呢?另外,我的魔塔是竖屏的,可以在最上面放一条广告(虽然很多人吐槽,不过程序员也需要吃饭啊…Orz)。

首先介绍下需要用到的插件,我这里不提供下载,相信大家能够自己解决的。

  1. HOTWEEN

  2. plyGame中的DiaQ

  3. Rotorz Tile System

  4. EasyTouch

  5. EasySave

使用HOTWEEN来处理主角的移动,DiaQ来处理对话系统,Rotorz来制作格子地图,EasyTouch用来处理触摸操作,EasySave用来处理存档系统。

整个魔塔我主要分为了8个脚本来控制:

  1. 主角操作

  2. 动作管理

  3. 声音管理

  4. 对话管理

  5. 游戏数据管理

  6. 游戏逻辑管理

  7. 任务管理

  8. 地图管理

下一篇博客会从主角的操作开始,逐一介绍魔塔的制作方法。

素材地址:http://pan.baidu.com/s/1c04j8Q0  密码:9ix3


Unity中的Raycast射线检测函数解析


在使用Unity引擎重新制作魔塔的时候,为了提高游戏性能,启用了新的方法。

其中用到了Raycast射线检测,而在最开始,检测出来总是有问题,有墙的地方检测正常,但是没墙的地方也会检测到碰撞,经过仔细查看,发现是之前太粗心,没有注意到Raycast的参数,于是记下此笔记。

Physics2D.Raycast

参数

origin 射线发射的原点
direction 射线发射的方向
distance 射线发射的距离
layerMask Layer层过滤
minDepth 射线检测Z坐标的最小值
maxDepth 射线检测Z坐标的最大值

之前由于粗心,一直以为参数和DrawLine的差不多,莫名其妙的折腾很长时间。

DrawLine的参数,第一个是起点,第二个是终点。而Raycast,第一个是起点,第二个是方向,第三个是长度。值得注意的是,方向是第二个参数的点的位置相对于起点的方向。当搞清楚这三个参数后,就简单的多了。

public bool checkTile(int x,int y)
{
    Vector2 position = new Vector2(x, y);
    RaycastHit2D hit = Physics2D.Raycast(position, Vector2.zero);
    Debug.DrawLine(new Vector3(x - 0.2f, y, 0), new Vector3(x + 0.2f, y, 0),Color.red);
    if (hit.collider != null)
    {
        print(hit.collider.gameObject.name);
        print("no");
        return false;
    }
    else
    {
        print("ok");
        return true;
    }
}

2DToolkit官方文档中文版打地鼠教程(十二):添加文本


我们的游戏已经可以玩了。地鼠从洞里钻出来,打中他们后,会消失在一朵烟里。但你想知道你游戏玩得怎么样,就需要在屏幕上显示分数。

我们使用2D Toolkit中的一个演示字体,你也可以使用自己喜欢的字体包

在我们添加分数文本之前,我们需要确保他能正确显示在屏幕上。我们添加一个anchor在我们的摄像机中,让分数文本能够更简单的定位。

  1. 在Hierarchy窗口,选中摄像机对象,并点击Create > tk2d > Camera Anchor。

  2. 在Hierarchy窗口中,一个Anchor对象被添加为摄像机的子对象。

  3. 我将把分数文本显示在游戏窗口的右上角,所以让场景的anchor到右上角。因此在Inspector窗口中的Tk 2d Camera Anchor(Script)的下面,点击Anchor下拉列表并选中UpperRight。注意设置完之后anchior的x和y会被设置为摄像机的宽和高,那就是屏幕的右上角。

  4. 我们的分数文本的位置将会相对于anchor,在Hierarchy窗口中选中Anchor,然后点击Create > tk2d > TextMesh。

  5. 在Hierarchy窗口中选中TextMesh,在Inspector窗口中你可以改变它的各种设置,这里需要修改的仍然是Anchor锚点。由于文本默认从左到右显示,如果我们选择TopRight,那么文本在屏幕上的位置将会改变。在分数变得更大时,为了容纳更多文本,它会变得更大。所以为了使分数显示在同一个地方,我们选择Upper Left作为Anchor设置。

    从字体下拉列表中选择GradientFont,并设置它的Max Chars值,这里设置为11,这包括了Score这个词和后面的分数。注意,MaxChars值设置过大会浪费内存,设置过小会导致文本被裁剪。在文本框中输入一些文本,我们预览文本的显示效果,随后我们会在脚本中控制文本内容。

    在TextureGradient中选择一个你喜欢的效果,我这里选择desert-horizon。

    默认字体很小,所以我们需要对文本进行缩放以显示在屏幕上适合的大小,我们这里选择 600 * 500。

  6. 现在,我们有了显示分数的文本,我们需要能够改变分数文本。在Project窗口中,创建一个C#脚本命名为ScoreScript,并将这里的代码粘贴进去。ScoreScript让我们能够很简单的获取及修改current score分数值。

  7. 在Project窗口中,选中ScoreScript并将它拖动到Hierarchy窗口中的ScoreText对象上。脚本将会被到添加到score文本对象中。

  8. 当打中一个地鼠后,增加10分,但没有打中时,扣去5分。变量和函数我们已经写在了脚本中,ScoreScript类使用它时并不需要创建实例,我们可以简单的使用:

    ScoreScript.Score += 10;

    来更新分数。 因此当打中地鼠后( if(mole.sprite.gameObject.activeSelf && mole.ColliderTransform == hit.transform) ),我们通过在MainGameScript增加下面的代码来控制分数。我们将使用的方法的核心代码是:

    ScoreScript.Score += mole.Whacked ? 0 : 10;

    如果洞中的地鼠已被打中过,则0分。


本系列教程的所有链接:


2DToolkit官方文档中文版打地鼠教程(十三):为游戏添加声音


现在我们为游戏添加一些音效。当地鼠被打中、从洞里钻出以及钻回洞中时会播放。我们在Projects > Mole > Sounds中已经有了这些音效。关于可用和不可用的音效格式,你可以查看这里

将声音片段添加到游戏中很容易!

  1. 取消每一个音效的3D Sound选项。

  2. 为beat_mole音效在MainGameScript添加一个AudioClip变量:

    public AudioClip moleHit;

  3. 然后在打中地鼠的代码中,也就是分数增加的代码,增加如下代码:

    AudioSource.PlayClipAtPoint(moleHit, new Vector3());

    此函数接受一个AudioClip和Vector3作为参数。Vector3对3D音效有效果,但我们这里使用的2D声音,所以我们只新建一个空的Vector3。

  4. 现在在Hierarchy窗口中选中MainGameScript,将会出现一个MoleHit音效字段,我们可以将音效拖到上面。
    img/gamescript_audio_clip.png

  5. 添加mole_down和mole_up音效,在MoleScript脚本下声明变量。

    public AudioClip moleUp;

    public AudioClip moleDown;

  6. 在MoveUp函数的最开始,循环之前,添加以下代码:

    AudioSource.PlayClipAtPoint(moleUp, new Vector3());

    在MoveDown函数最结尾,循环之后,添加以下代码:

    AudioSource.PlayClipAtPoint(moleDown, new Vector3());

  7. 同样,如果我们选中MoleUnit,在Inspector窗口中的MoleScript下面将会可以添加mole_up和mole_down音效。为每一个MoleUnit重复上述步骤。

  8. tk2dCamera应该已经带有Listener,没有Listener,我们将无法听到任何声音。如果您使用的是旧版本的tk2dcamera没有附带Listener,或正在使用Unity相机,需要添加Listener:在Hierarchy窗口中,选中tk2dcamera对象,然后在Inspector窗口点击AddComponent按钮,然后选择Audio > Audio Listener,Listener将被添加到相机,这就是所有你需要完成的事。
    img/add_listener.png

现在我们已经做完开发一个游戏的所有工作了。好吧,虽然它不是Halo 4这样的游戏,但我们已经覆盖了创建游戏的所有要点:添加一个相机、添加精灵、添加文本、添加声音和脚本。如果你没有游戏美工和游戏音乐师,不用担心,网上有很多的资源你可以下载下来用到后续的游戏制作中。


本系列教程的所有链接:


2DToolkit官方文档中文版打地鼠教程(十):播放精灵动画


之前我们已经创建好了动画剪辑,我们可将动画精灵添加到场景了。

  1. 在Hierarchy窗口,点击Create > tk2d > Sprite With Animator。

  2. 在Inspector窗口中,为精灵动画设置一个名字:BigDust。

  3. 在Tk 2d Sprite组件中将Collection设置为Dust,将Sprite设置为Animated_Dust_01。

  4. 在Tk 2d Sprite Animator组件中,将Anim Lib值设置为刚才创建的DustAnim。因为我们只想在地鼠被敲时播放动画,所以不选中Play automatically复选框,选中会让动画在游戏开始时立即播放。动画的位置在这里并不重要,我们会在地鼠被敲时动态的改变它的位置。


本系列教程的所有链接:


2DToolkit官方文档中文版打地鼠教程(十一):游戏脚本


现在是时候来完成游戏的所有功能了,尽管我们已经控制了每一个地鼠,但我们仍然需要一些东西来控制整个游戏。

  1. 在Hierarchy窗口,点击Create > tk2d > Empty Game Object并命名为GameScript。

  2. 在Project窗口中,选中MainGameScript并拖拽到Hierarchy窗口的GameScript对象上。

  3. 这个脚本包含游戏摄像机的公共变量,以及一个动画。选中并拖拽摄像机到Game Cam字段中,同样拖拽BigDust到Dust Animator字段中。

如果你想了解游戏脚本是如何工作的,点击这里

现在运行游戏,你就可以看到地鼠从洞里钻出,而你也可以敲他们了。被敲到的地鼠的精灵图片会改变,同时dust animation动画被播放。


本系列教程的所有链接:


2DToolkit官方文档中文版打地鼠教程(九):创建精灵动画


到目前为止,除了Dust精灵集合,我们已经使用精灵创建了很多精灵集合。这些精灵与我们打地鼠时一个接一个显示的不同,它是一个动画。

  1. 在Project窗口,点击Create > Folder创建文件夹并命名为Animations。

  2. 选中Animations文件夹,点击Create > tk2d > Sprite Animation并命名为DustAnim。

  3. 选中DustAnim对象,点击Inspector窗口中的Open Editor按钮。

  4. 在新打开的Open Editor窗口中,点击Create > Clip创建动画剪辑。

  5. 将刚创建的动画剪辑命名为DustCloud,在Collection字段选择精灵集合Dust,然后设置Sprite字段为Animated_Dust_01。

  6. 点击Autofill 1..9按钮,将会自动添加后续编号的剪辑到动画剪辑中。

  7. 之后再Wrap Mode字段,选择Once。这个设置的意思是每次动画被触发时,动画只播放一遍然后停止。

  8. 最后,点击Play按钮来对动画进行预览。你可通过修改frame和time设置来对动画播放的快慢进行调整。


本系列教程的所有链接: