[Unity3Dゲーム開発]、「パックマン」のゲームのチュートリアル

免責事項:この記事はブロガーオリジナル記事です、続くBY-SAのCC 4.0を著作権契約、複製、元のソースのリンクと、この文を添付してください。
このリンク: https://blog.csdn.net/q764424567/article/details/100038786

I.はじめに

パックマンのゲームを作る内部レッツユニティは、オリジナルのパックマンのゲームは、1980年10月にリリースされ、すぐにこれまでで最も有名なアーケードゲームとなりました。このゲームは非常に人気があり、さらにそのユニティゲームエンジン会社はそれのほんの一部に追加されます。
ここに画像を挿入説明
このチュートリアルでは、パックマンのゲームを作る、ユニティ2D強力な機能を使用しますが、我々はしようとします誰もがそれを理解できるように、いつものように、できるだけ簡単に解釈すること、コードを簡単に。

第二に、図の効果。

ここに画像を挿入説明

第三に、チュートリアル

注:バージョン番号、Unity5.0.0f4

1.カメラの設定

私たちは、階層内のメインカメラと黒に背景色を選択します。下図のように我々はまた、大きさや位置を調整します。
ここに画像を挿入説明

2.背景の設定
ここに画像を挿入説明
保存は、我々はスプライトプロジェクトフォルダを入れて
ここに画像を挿入説明
、そのプロパティを変更します。画像をクリックしてください- >インスペクタパネル
ここに画像を挿入説明
注:ピクセル単位当たりの単位画素値を意味し、8に設定されているゲームの世界では8×8画素単位のために。私たちは、テクスチャのすべてとして、この値を使用します。2点間の距離が常に8であるので、我々は8を選びました。私たちは、後で簡単にピボットを揃えているので、左下隅のこれを選びました。

その後、我々は、シーンに画像をドラッグして、ゼロに戻ります:
ここに画像を挿入説明
ここに画像を挿入説明
物理的な効果を追加:
BoxColliderに追加された各壁に、すべての壁BoxColliderはすべて、時間が非常に正確でなければなりませんか、他ゲームを開始することに留意すべきですあなたは迷路内に閉じ込められていてもよいです。

3.吃豆人设置
吃豆人的运动方向不同,状态也不同,让我们在一个图像中绘制所有动画,其中每行有一个动画:
ここに画像を挿入説明
将图片保存到Sprite文件夹中。

我们需要将图片处理一下,将这样图片做一下切片:
点击图片
ここに画像を挿入説明
Texture Type设置成Sprite
Sprite Mode设置成Multiple
点击Apply

然后点击Inspector面板上的Sprite Editor进行切片:
把它切成16X16的网格,点击Apply
ここに画像を挿入説明

制作动画

现在我们有了动画切片,就可以用它创建4个动画,分别是:

  • 右(边),正确的(切片0、1和2)
  • 左边(第3、4及5段)
  • 向上(第6、7及8段)
  • 降下来(第9、10及11片)

选择前三个切片,拖入到场景中:
ここに画像を挿入説明
Unity就会提示我们在哪里保存动画
(https://noobtuts.com/content/unity/2d-pacman-game/pacman_animation_right_slices_toscene.png)]
我们重复这个动画,将动画制作完毕。

动画状态机

我们有4个动画文件,但是还要知道什么时候播放动画,所以我们需要一个动画状态机,它有4个状态:

  • 右(边),正确的
  • 左边
  • 向上
  • 降下来

我们在状态机里面创建一个Float类型的参数,用来控制状态:

如果DirY>0然后去向上 (如DirY=1,DirY=2,DirY=3等等)
如果DirY<0然后去降下来 (如DirY=-1,DirY=-2,DirY=-3等)
如果DirX>0然后去右(边),正确的 (如DirX=1,DirX=2,DirX=3等等)
如果DirX<0然后去左边 (如DirX=-1,DirX=-2,DirX=-3等等)

稍后我们在代码中会用到他们:

GetComponent<Animator>().SetFloat("DirX", 0);
GetComponent<Animator>().SetFloat("DirY", 0);

过渡效果
我们希望Unity能够根据这些参数自动切换到不同的动画状态。过渡就是用来实现这一点的。例如,我们可以从左边到右(边),正确的条件是DirX>0…然而,它被认为是最好的做法,有一个小的错误容差,因为浮点比较并不总是完美的,因此我们将使用DirX>0.1相反。

现在我们还必须使用DirX>0.1从其他状态过渡到右(边),正确的…为了避免我们做这些工作,我们可以使用Any State

这个Any State代表任何一个状态。所以如果我们从任何装啊提到右(边),正确的然后,这与创建一个从左边, 向上和降下来到右(边),正确的.

让我们右键单击任何状态并选择过渡…之后,我们可以将白色箭头拖到右(边),正确的状态:
ここに画像を挿入説明
在Inspector面板中设置动画的参数:
ここに画像を挿入説明
注意:这避免了在按住移动键的同时,动画总是被重新启动的奇怪情况。

因此,每当吃豆人走到右边(DirX>0.1),动画师将切换到右(边),正确的状态并播放动画。

我们将增加3个过渡:

  • 任何状态到左边有条件DirX<-0.1
  • 任何状态到向上有条件DirY>0.1
  • 任何状态到降下来有条件DirY<-0.1

我们几乎完成了我们的动画状态机。这里还有最后一个调整,所以让我们选择所有的状态,然后修改它们的速度在Inspector这样动画看起来就不会太快了:

ここに画像を挿入説明
添加吃豆人

添加物体状态
BoxCollider2D
添加刚体
Rigidbody2D

移动脚本
PacmanMove.cs

using UnityEngine;
using System.Collections;

public class PacmanMove : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

这个启动函数在开始游戏时由统一自动调用。这个更新函数被一次又一次地自动调用,大约每秒60次。(这取决于当前的帧速率,如果屏幕上有很多东西,它可能会更低).
还有另一种类型的更新函数,即修正更新…它也被一次又一次的调用,但是在一个固定的时间间隔内。单位物理是在完全相同的时间间隔内计算的,所以使用它总是一个好主意修正更新在做物理工作时:

using UnityEngine;
using System.Collections;

public class PacmanMove : MonoBehaviour {

    void Start() {

    }

    void FixedUpdate() {

    }
}

我们需要一个公共变量,以便稍后可以修改移动速度:

using UnityEngine;
using System.Collections;

public class PacmanMove : MonoBehaviour {
    public float speed = 0.4f;

    void Start() {

    }

    void FixedUpdate() {

    }
}

我们还需要一种方法来找出行动人员是否可以移动到某个方向,或者是否有一堵墙。所以举个例子,如果我们想知道帕克曼的顶端是否有一堵墙,我们可以简单地投线从一个单位以上的帕克曼到帕克曼,看看它是否击中任何东西。如果它击中了帕克曼本人,那就没有中间的任何东西,否则肯定有一堵墙。

以下是功能:

bool valid(Vector2 dir) {
    // Cast Line from 'next to Pac-Man' to 'Pac-Man'
    Vector2 pos = transform.position;
    RaycastHit2D hit = Physics2D.Linecast(pos + dir, pos);
    return (hit.collider == GetComponent<Collider2D>());
}

注:我们只是从Pac-Man旁边的点抛出这条线(pos+dir)给帕克曼本人(pos).
我们还需要一个功能,使Pac-Man移动1单位到所需的方向.当然,我们可以这样做:

transform.position += dir;

但这看起来太突然了,因为这是一个巨大的进步。相反,我们希望他能一帆风顺地去那里。让我们将移动目的地存储在变量中,然后使用修正更新一步地走向目的地:

using UnityEngine;
using System.Collections;

public class PacmanMove : MonoBehaviour {
    public float speed = 0.4f;
    Vector2 dest = Vector2.zero;

    void Start() {
        dest = transform.position;
    }

    void FixedUpdate() {
        // Move closer to Destination
        Vector2 p = Vector2.MoveTowards(transform.position, dest, speed);
        GetComponent<Rigidbody2D>().MovePosition(p);
    }

    bool valid(Vector2 dir) {
        // Cast Line from 'next to Pac-Man' to 'Pac-Man'
        Vector2 pos = transform.position;
        RaycastHit2D hit = Physics2D.Linecast(pos + dir, pos);
        return (hit.collider == GetComponent<Collider2D>());
    }
}

注:我们使用GetComponent来访问Pac-Man的刚体组件。然后,我们使用它来进行移动(我们不应该使用Transform.Place来移动具有刚体的GameObjects)。
当我们不移动时,我们也要注意箭头键的按压。
注意:如果当前位置等于目的地,我们就不会移动。

下面是具有输入检查的FixedUpdate函数:

void FixedUpdate() {
    // Move closer to Destination
    Vector2 p = Vector2.MoveTowards(transform.position, dest, speed);
    GetComponent<Rigidbody2D>().MovePosition(p);

    // Check for Input if not moving
    if ((Vector2)transform.position == dest) {
        if (Input.GetKey(KeyCode.UpArrow) && valid(Vector2.up))
            dest = (Vector2)transform.position + Vector2.up;
        if (Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right))
            dest = (Vector2)transform.position + Vector2.right;
        if (Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up))
            dest = (Vector2)transform.position - Vector2.up;
        if (Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right))
            dest = (Vector2)transform.position - Vector2.right;
    }
}

注:变换位置铸成矢量2因为这是比较或添加另一个Vector 2的唯一方法。也-向量2.右手段左边和-向量2.向上手段降下来.

设置动画参数
现在,我们可以很好地将Pac-Man移动到迷宫中,但是动画师还没有播放所有的动画。没问题,让我们修改代码来计算当前的移动方向,然后设置动画参数:

void FixedUpdate() {
    // Move closer to Destination
    Vector2 p = Vector2.MoveTowards(transform.position, dest, speed);
    GetComponent<Rigidbody2D>().MovePosition(p);

    // Check for Input if not moving
    if ((Vector2)transform.position == dest) {
        if (Input.GetKey(KeyCode.UpArrow) && valid(Vector2.up))
            dest = (Vector2)transform.position + Vector2.up;
        if (Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right))
            dest = (Vector2)transform.position + Vector2.right;
        if (Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up))
            dest = (Vector2)transform.position - Vector2.up;
        if (Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right))
            dest = (Vector2)transform.position - Vector2.right;
    }

    // Animation Parameters
    Vector2 dir = dest - (Vector2)transform.position;
    GetComponent<Animator>().SetFloat("DirX", dir.x);
    GetComponent<Animator>().SetFloat("DirY", dir.y);
}

注:我们计算了当前的移动。迪尔基本向量数学。我们所要做的就是减去电流位置从长子理性。

将吃豆人放在最前面

在我们开始研究Pac-Dot之前,我们应该确保Pac-Man总是被吸引到他们的前面。我们正在做一个2D游戏,所以没有真正的Z就像3D游戏一样。这意味着,统一只会随意绘制对象。让我们确保团结总是在其他一切面前吸引帕克曼。

有两种方法可以做到这一点。我们可以要么更改雪碧渲染器氏分选层属性,或者我们可以更改层序财产。这个分选层对于拥有更多物品的大型游戏来说是很重要的。对我们来说,只要简单地更改层序到1:
Pac-Man SpriteRenderer Order in Layer
注意:统一绘制对象按其顺序排序。它以最低的顺序开始,以更高的顺序继续。所以,如果帕克-曼有第一项命令,那么他总是在迷宫、食物和其他任何有0顺序的东西之后抽签。因为他喜欢其他的东西,所以他会自动地排在其他的前面

4.小圆点设置
可以吃的小圆点叫做“点”(pac-dots)。有些人现在可能是“水果”或者只是“食物”。我们先画一张小画2x2PX图像的Pac-点:
ここに画像を挿入説明
将这个图片保存下来,拖到项目的Sprite文件夹中

然后拖到场景中,添加BoxCollider组件

让我们为这个小圆点添加一个脚本:
Pacdot.cs

using UnityEngine;
using System.Collections;

public class Pacdot : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

我们不需要启动或者更新函数,所以让我们移除这两个函数。相反,我们将使用前面提到的OnTriggerEnter2D职能:

using UnityEngine;
using System.Collections;

public class Pacdot : MonoBehaviour {

    void OnTriggerEnter2D(Collider2D co) {
        // Do Stuff...
    }
}

判断一下,穿过小圆点的是否是吃豆人,是的话就销毁自身:

using UnityEngine;
using System.Collections;

public class Pacdot : MonoBehaviour {

    void OnTriggerEnter2D(Collider2D co) {
        if (co.name == "pacman")
            Destroy(gameObject);
    }
}

然后我们复制豆豆,将它像场景中摆放的一样,摆放一下:

ここに画像を挿入説明
5.敌人设置

红鬼形象:
和往常一样,我们将从画一个幽灵Sprite开始,里面有所有的动画。每一行将包含一个动画:

  • 右(边),正确的
  • 左边
  • 向上
  • 降下来

第一个将是红色的鬼魂,也被称为布林基:
ここに画像を挿入説明
像上面一样进行以下处理:
切片成16X16网格
创建动画
创建动画状态机
设置状态参数

然后将它拖到场景中,添加物理BoxCollider2D

敌人的移动脚本:GhostMove.cs

using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

我们不需要启动或者更新函数,相反,我们将使用修正更新功能(因为它是为了运动之类的物理材料而设计的):

using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {

    void FixedUpdate () {

    }
}

让我们增加一个公众变换数组,以便稍后可以在检查器中设置路径点:

using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {
    public Transform[] waypoints;

    void FixedUpdate () {

    }
}

注意:数组意味着它不仅仅是一个转换。
我们还需要某种索引变量来跟踪敌人当前走向的路径点:

using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {
    public Transform[] waypoints;
    int cur = 0;

    void FixedUpdate () {

    }
}

注意:当前的路径点总是可以用路标[路].
当然还有一个运动速度变量:

using UnityEngine;
using System.Collections;

public class GhostMove : MonoBehaviour {
    public Transform[] waypoints;
    int cur = 0;

    public float speed = 0.3f;

    void FixedUpdate () {

    }
}

现在我们可以使用修正更新函数使其更接近当前路径点,或在到达时立即选择下一个路径点:

void FixedUpdate () {
    // Waypoint not reached yet? then move closer
    if (transform.position != waypoints[cur].position) {
        Vector2 p = Vector2.MoveTowards(transform.position,
                                        waypoints[cur].position,
                                        speed);
        GetComponent<Rigidbody2D>().MovePosition(p);
    }
    // Waypoint reached, select next one
    else cur = (cur + 1) % waypoints.Length;
}

注意:我们使用了矢量2.运动塔函数来计算一个点,该点离路径点有点近。之后我们把鬼魂的位置刚体2D.移动定位…如果到达路径点,则增加库尔一分为二。我们还想重新设置库尔到0如果它超过列表长度。我们可以用这样的方法如果(cur=waypoints.length)cur=0,但是使用模块(%)操作符使这个看起来更优雅一些。
我们也不要忘记设置动画参数:

void FixedUpdate () {
    // Waypoint not reached yet? then move closer
    if (transform.position != waypoints[cur].position) {
        Vector2 p = Vector2.MoveTowards(transform.position,
                                        waypoints[cur].position,
                                        speed);
        GetComponent<Rigidbody2D>().MovePosition(p);
    }
    // Waypoint reached, select next one
    else cur = (cur + 1) % waypoints.Length;

    // Animation
    Vector2 dir = waypoints[cur].position - transform.position;
    GetComponent<Animator>().SetFloat("DirX", dir.x);
    GetComponent<Animator>().SetFloat("DirY", dir.y);
}

太好了,还有最后一件事要添加到我们的脚本中。鬼魂和帕克曼相撞时应该把他杀死:

void OnTriggerEnter2D(Collider2D co) {
    if (co.name == "pacman")
        Destroy(co.gameObject);
}

注意:请随意减少病人的生命或显示游戏结束屏幕就在这一点上。

6.セットウェイポイント
の敵にいくつかのパスポイントを追加してみましょう、良いです。私たちは、ゲームオブジェクトを選択します- >上のメニューから空を作成します。我々はそれを名前を変更し、その後、我々はより簡単にシーンを見ることができるようにBlinky_Waypoint 0を割り当てます:
ここに画像を挿入説明
注意:あなたがゲームをプレイするときだけ、視覚補佐官ギズモは、我々はそれを見ることはありません。

:私たちは...今、次の(15、20)にそれを置くここに画像を挿入説明
現在ウェイポイントのコピー、ブリンキーウェイポイント1の名前を変更できます(10、20)にそれを置くされていますここに画像を挿入説明
...(10、14ウェイポイント2、その後、ブリンキーに):ここに画像を挿入説明
私たちが移動すると、スクリプトが自動的に到着する最後の、最初のウェイポイントの後に行くし続ける、我々は完璧な円を持っています。

私たちは別のウェイポイント私たちのスロットGhostMoveスクリプト後の階層の道路ポイントの上に敵して、いずれかを選択してみましょう:

7.プレイ

我々はプレーをクリックして、私たちは、ゲームを開始することができます

8.拡張

今、ゲームをより面白くするために、読者まで。あなたは、機能の多くを追加することができます。

  • 音楽
  • 分数
  • 高度なAI
  • インターフェースを開始します
  • アップグレード
  • 人生の価値
  • 小道具賞
  • その他のアニメーション

おすすめ

転載: blog.csdn.net/q764424567/article/details/100038786