您的位置:首页 > 移动开发 > Unity3D

Unity笔记 2D ROGUELIKE 实例详解

2015-08-26 14:24 555 查看
这是unity官方的一个视频教程。比较完整的一个2D小游戏,共14讲。

http://unity3d.com/learn/tutorials/projects/2d-roguelike

这里整理了一些tips。

游戏控制一个2D帧动画的人物在tile地图上行走,目的是走到出口。地图上有食物、敌人、阻挡。移动消耗食物,碰到敌人会自动攻击。

Unity把这个项目定义为中级,可以先看这个

第一部分 准备资源

1 介绍

tag、layer、sortinglayer的用法:

tag 是标识gameobject用的。

layer 一共32个不能扩展,类似分类的概念。射线选择和渲染可以指定感兴趣的layer。

sortinglayer 在render里选择的,决定2D排序用的。

2 player和enemy的prefab

制作prefab:

prefab就是gameobject的预置模板,建立好后方便clone建立gameobject无论是在编辑时和脚本中。

player的prefab涉及加精灵动画、做AnimationController(AC)、选tag、layer、sortinglayer、加Rigibody控制用。

加精灵动画:

这个游戏是简单的帧动画,美术资源已经按照固定大小做好了每帧画面。

选中一个动作的所有帧拖到gameobject就会生成一个动画文件(这时有对话框要起文件名和选路径)和一个配套的AnimationController。

AnimationController(AC):

例子里用AC调整了动画速度。

2种敌人公用一个AC使用Create Animator Override Controller 这种AC会要求指定一个AC状态机和选择override动画。

添加rigidbody来控制:

选中IsKinematic这样就不会被物理系统本身影响,但可以使用其控制gameobject。

3 Tile(地形块)prefab

为每个Tile元素建立perfab:

Tile prefab没有动画的情况(只使用单帧资源)添加一个SpiriteRender然后从资源里拖一帧上去。

注意和有动画的tile(可以打碎的tile)区分。

trigger:

Exit、Food之类的要交互,加上Box Collider并设置trigger这样就可以重叠而不受物理系统约束了。

wall之类的trigger就不设置了,要利用物理系统达到Player不能重叠墙的效果。

第二部分 关卡和整体结构

1 board manager

管理关卡场景的脚本。

生成地图、放置元素等功能。

2 game manager

编辑状态数组批量赋值:

可以点击右边图标lock inspector,多选prefab拖动到脚本public的gameobject数组上。

singleton的一个实现:

singleton简单说就是个全局变量。

public static GameManager instance = null;
//Awake is always called before any Start functions
void Awake()
{
//Check if instance already exists
if (instance == null)
//if not, set instance to this
instance = this;
//If instance already exists and it's not this:
else if (instance != this)
//Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
Destroy(gameObject);

//Sets this to not be destroyed when reloading scene
DontDestroyOnLoad(gameObject);
}


建立各种singleton的manager合适地方:

Loader脚本组织各种Singleton,把loader脚本放在maincamera上,awake时建立singleton(gamemanager、soundmanager)

代码如下:

void Awake ()
{
//Check if a GameManager has already been assigned to static variable GameManager.instance or if it's still null
if (GameManager.instance == null)
//Instantiate gameManager prefab
Instantiate(gameManager);
}


第三部分 游戏逻辑实现

1 移动基类

建立一个MovingObject的基类:

abstract virutal override 关键字的使用。

使用StartCoroutine建立协程来,移动object。

generic泛型方法定义了尝试移动的过程,由子类来决定自己关心的地形物体和处理方法。

本集是整个教程难点,涉及c#的相关知识比较多,看过这些就没问题啦 c#笔记 系列

限制碰撞涉及的layer建立一个layerMask:

public LayerMask blockingLayer;

2 wall的脚本

没有动画的gameobject改变外观:

gameobject通过spriterenderer替换sprite

//Set spriteRenderer to the damaged wall sprite.

spriteRenderer.sprite = dmgSprite;

区别于有动画的方式,有动画的直接AC改变动画。

3 Player AC

改变动画:

添加一个 trigger类型的参数用于变换动画(transition)的条件。

idle to chop :

transition to 0,通常帧动画处理方式,没有过渡。

has exit time,不勾选,在idle过程中随时插入chop动画。

chop to idle:

transition to 0,通常帧动画处理方式,没有过渡。

has exit time,勾选,idle不可打断chop。

exit time,设置1,完成1次才可播放idle。

4 Player的脚本

利用singleton保存数据:

GameManager.instance.playerFoodPoints = food;

重载2个泛型方法完成move,调用并强转为感兴趣的交互类型Wall:

protected override void AttemptMove (int xDir, int yDir)

protected override void OnCantMove (T component)

AttemptMove (horizontal, vertical);

Wall hitWall = component as Wall;

播放动画:

animator.SetTrigger (“playerChop”);

用tag配合这个回调来处理游戏逻辑:

private void OnTriggerEnter2D (Collider2D other)

延迟触发一个函数调用:

Invoke (“Restart”, restartLevelDelay);

5 Enemy的脚本

和player差不多,多了个自动找player的简单算法MoveEnemy。

6 Enemy AC 和 GameManager相关修改

Enemy AC:

和Player很像。由于用了override AC 2种enemy都ok了。

GameManager Update里Start协程完成移动enemy功能。

GameManager添加EnemyList,每个enemy 在Start的时候利用GameManagerSingleton把自己加进list:

GameManager.instance.AddEnemyToList (this);

第四部分 收尾

1 UI和level

UGUI系统:

canvas是UI元素容器。各种Anchor(居中、贴底等)可以用于对齐UI元素。

得到UI的gameobject设置其显示或隐藏。

得到UI的Component, Text levelText,直接设置它的值。

OnLevelWasLoaded:

当Scene被装载后调用。

2 音乐和音效

建立一个singleton的SoundManager

加2个audiosource,一个是audio(efx音效)一个是music(音乐)

在player加上public audioclip(在编辑器指定),在需要的时候播放。

3 手机控制

intput touch的使用见代码:

private Vector2 touchOrigin = -Vector2.one;

//Check if Input has registered more than zero touches
if (Input.touchCount > 0)
{
//Store the first touch detected.
Touch myTouch = Input.touches[0];

//Check if the phase of that touch equals Began
if (myTouch.phase == TouchPhase.Began)
{
//If so, set touchOrigin to the position of that touch
touchOrigin = myTouch.position;
}

//If the touch phase is not Began, and instead is equal to Ended and the x of touchOrigin is greater or equal to zero:
else if (myTouch.phase == TouchPhase.Ended && touchOrigin.x >= 0)
{
//Set touchEnd to equal the position of this touch
Vector2 touchEnd = myTouch.position;

//Calculate the difference between the beginning and end of the touch on the x axis.
float x = touchEnd.x - touchOrigin.x;

//Calculate the difference between the beginning and end of the touch on the y axis.
float y = touchEnd.y - touchOrigin.y;

//Set touchOrigin.x to -1 so that our else if statement will evaluate false and not repeat immediately.
touchOrigin.x = -1;

//Check if the difference along the x axis is greater than the difference along the y axis.
if (Mathf.Abs(x) > Mathf.Abs(y))
//If x is greater than zero, set horizontal to 1, otherwise set it to -1
horizontal = x > 0 ? 1 : -1;
else
//If y is greater than zero, set horizontal to 1, otherwise set it to -1
vertical = y > 0 ? 1 : -1;
}
}

#endif //End of mobile platform dependendent compilation section started above with #elif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  2d项目 unity 详解