Unity学习笔记3 简易2D横版RPG游戏制作(三)

这一篇本来应该是在上一篇后面直接补进去的。不过因为排版的问题。所以我就另开一篇来整理了,好了,废话不多说,马上整理:

十八、关卡的锁定与解锁

前面我们已经解决了在游戏开始时可以选择关卡的问题,接下来我们就要解决下一个问题了。那就是游戏关卡的解锁问题。玩过像愤怒的小鸟、植物大战僵尸等等的游戏的人应该知道,很多游戏都有这样的设定,在你还没有开始过完第一关的时候,是不可以玩第二关和第三关的。有些游戏需要这种功能,有些游戏则不需要。不管需不需要,我们在这里就顺便把这个问题也给研究一下吧:

首先先想一下思路,思路当然就是制作一个变量,来进行判定,如果过了某一关,某个变量就会变成某个值,那么打开下一关的按钮就可以顺利执行了。现在我们来处理一下之前的Door脚本,我就整个贴出来吧:

using UnityEngine;
using System.Collections;


public class Door : MonoBehaviour {


    public string Level;
    GameManager gameManager;
    GameObject gameObjectGM;


    void Start() {
        gameObjectGM = GameObject.FindGameObjectWithTag("GameManager");
        gameManager = gameObjectGM.GetComponent<GameManager>();
    }


    void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
        {
           gameManager.SaveGame();
           PlayerPrefs.SetInt("LevelUnlock",2);
           Application.LoadLevel(Level); 
         } 
     }
}

在这个脚本里面,我主要是加了这么一句:PlayerPrefs.SetInt("LevelUnlock", 2); 里面的这个LevelUnlock是自己随便起的一个变量,保存这个脚本之后,我们进入游戏场景1,然后撞击里面的door物体,就会在跳到下一个场景之前先保存这么一个变量。这里有个地方需要注意一下,PlayerPrefs.SetInt("LevelUnlock", 2);这句话最好是放在Application.LoadLevel(Level); 的前面,虽然我测试过,放在前面的话,载入新场景的时候它还是有顺便储存这么一个变量。但是有可能只是因为这句话比较短,在跳到下一个场景之前就已经执行完毕了。如果在Application.LoadLevel(Level); 这句话后面放了太多句话,让它根本没有时间去执行的话,它可能就没办法全部执行的。因为直接使用Application.LoadLevel(Level); 的话,在进入新场景的时候是会摧毁旧有场景的一切物体的,自然也包括他们身上的所有脚本了。所以在这个地方大家最好注意一下。

接着我们来到LevelSelect场景,打开摄像机上面的LevelSelect脚本,然后照下面这样编辑:

using UnityEngine;
using System.Collections;

public class LevelSelect : MonoBehaviour {

    int sw = Screen.width;
    int sh = Screen.height;

    public string Level1;
    public string Level2;

    void OnGUI(){
        //进入第一关的按钮
        if (GUI.Button(new Rect(0, 0, sw * .5f, sh), "第一关"))
        {
            Application.LoadLevel(Level1);
        }
                //判断是否进入第二关
        if (PlayerPrefs.GetInt("LevelUnlock", 0) >= 2)
        {
            if (GUI.Button(new Rect(sw * .5f, 0, sw * .5f, sh), "第二关")) {
                PlayerPrefs.DeleteKey("LevelUnlock");
                Application.LoadLevel(Level2);
            }
        }
        else
        {
            GUI.Box(new Rect(sw * .5f, 0, sw * .5f, sh),"未解锁\n"+ "第二关");
        }      
    }
}

GUI.Box就是显示一个无法点击的东西,这个就不解释了。当然,你用GUI.Button也是完全没问题的。如果LevelUnlock这个变量加载之后大于等于2的话,那么我们就可以启动第二关的GUI按钮功能,否则的话,第二关的按钮就会变成一个无法点击的东西。在没有进入第一关碰到那个door物体之前,如果直接载入LevelSelect场景的话,LevelUnlock值由于没有赋值,就是零,自然就不可能大于等于2了。这样就可以实现我们的解锁功能了。至于那句PlayerPrefs.DeleteKey("LevelUnlock");是用来删除某个已经保存的变量的。在这里我们可不是想把整个游戏存档给清理了,所以只需要使用DeleteKey功能而不是DeleteAll,千万不要弄错了哈哈。

这样清理掉这个变量仅仅是我在测试的过程中使用的。清理掉之后,如果我们再次载入LevelSelect,就会发现第二关的按钮又被锁定了。可能有人会说,这样搞有什么用?这样搞当然是很有用的。我们在制作游戏的过程中可能需要重复测试,自然不希望被某个已经保存了的变量干扰到我们的测试结果。而且,这种“反解锁”的idea可以用于一些比较狡猾的游戏设计:比如你在游戏里面连续输了太多次,或者是因为某些原因降级了,那么就可以通过这种设计方法,反锁你的某些已进入的关卡,或者是已经习得的技能。同样的,在一些任务和剧情设计中,有可能在某个特定时间上,你是不可以进入某个地方的。比如你的剧情设计自己的小镇外面突然来了一大堆进击的巨人,那么可能在巨人来袭的时候,玩家是不可以出城的,我们就可以在城门进行一个这样的设定,就打不开了。(不是每个人都是兵长,可以把巨人当白萝卜切割的嘛~~

下面我截了一些图来看看测试结果,我先把所有的存档给清空。然后将场景1复制多了一个,变成场景2,并且把这个场景2中的door物体删掉,用于区分两个场景。

首先我们来到Mainmenu场景,改一下摄像机的MainMenu脚本里面的Level,改成LevelSelect,这样是为了点击“载入游戏”时载入LevelSelect场景。

接下来运行:


然后我们点击载入游戏,我们看到的就是这样。因为还没弄画面,所以有点丑,不过我们先学好基本原理再说,大家见谅。(画面中间那个quad物体是我之前随便摆放进去的,不用管它,直接无视就行。)


接着我们点击左边的按钮进入我们熟悉的场景1,并且去碰撞我们的door物体。(我设定为深绿色的那个物体)


碰到之后我们会自动跳转到下一个场景,下一个场景是没有door物体的,刚刚我们删了,其他的和场景1一模一样。(因为下面的图是我事后截的图,游戏存档被我清理过一次,所以细心一些的朋友会发现左上角的红心减少了,其实你正常进入的话是不会影响你保存的curHealth的变量的。)

我们可以看到,深绿色的door不见了,我们已经来到场景2了。现在我们保存一下游戏,然后退出。然后重新进入到LevelSelect的画面中:



这一次,右边的第二关按钮已经解锁了。我们可以按第二关的按钮进入我们的游戏了!!到这里,解锁功能就做好了,我们马上点一下看看:


嗯,没错,是我们的第二关。

但是在实际操作中会有这样一个问题。因为我的设计是让载入第二关的瞬间就清理掉LevelUnlock的值,所以如果这样做的话,在那个LevelSelect画面进入第二关的画面的过程中,你会看到第二关的GUI.Button会变成GUI.Box,虽然只是一瞬间,但要是真的做成游戏的话,视觉体验上就会很差了。解决这个问题的最简单方法就是不要在那里删掉LevelUnlock这个变量,就不会出事。当然,如果你实在要这样做,也还有一个办法的,那就是让整个场景变暗,然后再重新亮起来,这样就可以实现我们要的效果了。关于淡入淡出效果,我还在研究,所以暂时没办法整理到教程里面来。请见谅。

好了,关于解锁关卡的研究就到这里,下面我们继续学习。

十九、金钱

如果说前面几讲稍微有点复杂的话,那么这一讲就实在是太简单了。这一讲其实就是增加一个简单的变量。因为我们暂时还没有涉及到交易系统,所以也没什么好说的。

首先呢,我们建一个变量,static public int curCoins = 0;这个是弄在我们的GameManager脚本里面的,就不再解释了。

接着修改void Awake(),在里面读取存档后增加一个curCoins = PlayerPrefs.GetInt("Player Coins");

修改Update()里面的这个:if (playerStats) {statsDisplay.text = "等级:" + level + "\n经验:" + curEXP + "/" + maxEXP + "\n攻击力:" + bulletDamage + "\n金钱" + curCoins;

这样的话在显示的时候就可以连“金钱”也一起显示出来。

SaveGame()里面增加PlayerPrefs.SetInt("Player Coins", curCoins);这一句。

然后保存我们的脚本。

为了大家方便,我将这个脚本重新贴出来:

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {
    //引用Controller2D
    public Controller2D controller2D;
    //角色生命值
    public Texture playersHealthTexture;
    //控制上面那个Teture的屏幕所在位置
    public float screenPositionX;
    public float screenPositionY;
    //控制桌面图标的大小
    public int iconSizeX = 25;
    public int iconSizeY = 25;
    //初始生命值
    static public int curHealth = 3;
    //游戏刚开始时,第一级的最大生命值,以后每级增加1。
    public int maxHealth = 3;

    GameObject player;
    //我们的子弹的攻击力
    static public int bulletDamage = 1;
    //当前经验值
    public int curEXP;
    //从第一级升到下一级需要的经验值(以后每一级增加50)
    int maxEXP = 50;
    //游戏刚开始时的等级
    int level = 1;
    //游戏刚开始的金钱
    static public int curCoins = 0;

    //这个布尔值用于判定是否显示角色的状态
    bool playerStats;
    //下面这个用于显示角色的状态
    public GUIText statsDisplay;
    //用于暂停的布尔值
    bool pauseMenu;
    //用于判断是否是否保存
    int saved = 0;
    //自动保存计时器
    public float autoSaveTimer;

    void Awake(){
        saved = PlayerPrefs.GetInt ("Game Saved");
        if (saved == 1) {
            curEXP = PlayerPrefs.GetInt ("Player EXP");
            level = PlayerPrefs.GetInt ("Player Level");
            curCoins = PlayerPrefs.GetInt("Player Coins");
            maxEXP = level * 50;
            maxHealth = level + 2;
            curHealth = maxHealth;
       } 
    }

    //这个地方定义了私有变量player作为一个GameObject,然后用下面的FindGameObjectWithTag获取它,这样的话,在下面的伤害判断时,就可以用player.renderer了。
    void Start(){
        player = GameObject.FindGameObjectWithTag("Player");
    }

    void Update(){
        //自动存档功能
        autoSaveTimer += Time.deltaTime;
        if (autoSaveTimer >= 10f) {
            SaveGame(); 
            print ("保存啦~~");
            autoSaveTimer =0;
        }

        if (Input.GetKeyDown (KeyCode.P)) {
            pauseMenu = !pauseMenu; 
        }

        if (curEXP >= maxEXP) {
            LevelUp();      
        }

        if (Input.GetKeyDown (KeyCode.C)) {
            playerStats = !playerStats;     
        }

        if (Input.GetKeyDown (KeyCode.E)) {
            curEXP += 10;       
        }

        if (playerStats) {
            statsDisplay.text = "等级:" + level + "\n经验:" + curEXP + "/" + maxEXP + "\n攻击力:" + bulletDamage + "\n金钱" + curCoins;
        } 
        else {
            statsDisplay.text = ""; 
        }

        if (curHealth > maxHealth) {
            curHealth = maxHealth;
        }
    }
    //这个函数用于角色升级
    void LevelUp(){
        curEXP = 0;
        maxEXP = maxEXP + 50;
        level++;

        //升级的功效
        maxHealth++;
    }
    

    //OnGUI函数最好不要出现多次,容易造成混乱,所以我把要展示的东西都整合在这个里面
    void OnGUI(){

        //控制角色生命值的心的显示
        for (int h =0; h < curHealth; h++) {
            GUI.DrawTexture(new Rect(screenPositionX + (h*iconSizeX),screenPositionY,iconSizeX,iconSizeY),playersHealthTexture,ScaleMode.ScaleToFit,true,0);
        }

        if (pauseMenu) {
            //“保存游戏”按钮
            if(GUI.Button(new Rect(Screen.width*.25f,Screen.height*.4f,Screen.width*.5f,Screen.height*.1f),"保存游戏")){
                SaveGame();
            }
            //“显示保存的数据”按钮
            if(GUI.Button(new Rect(Screen.width*.25f,Screen.height*.6f,Screen.width*.5f,Screen.height*.1f),"显示保存的数据")){
                print ("显示保存的数据");
                print("当前等级:"+ PlayerPrefs.GetInt("Player Level"));
                print("当前经验:"+ PlayerPrefs.GetInt("Player EXP"));
                print("是否保存:"+ PlayerPrefs.GetInt("Game Saved"));
            }
        }
    }

    public void SaveGame(){
        saved = 1;
        PlayerPrefs.SetInt("Player Level",level);
        PlayerPrefs.SetInt("Player EXP",curEXP);
        PlayerPrefs.SetInt("Player Coins", curCoins);
        PlayerPrefs.SetInt("Game Saved",saved);
        print ("已保存");
    }

    
    void PlayerDamaged(int damage){   //此处使用player.renderer.enabled来进行判断,如果角色没有在闪烁,也就是存在的状态为真,那么才会受到伤害,这样可以避免角色连续受伤,还有另外一种方法是采用计时,这里没有采用那种方法。
        if (player.renderer.enabled) {
                        if (curHealth > 0) {
                                curHealth -= damage;    
                        }

                        if (curHealth <= 0) {
                                RestartScene ();    
                        }
                }
    }

    void RestartScene(){
        Application.LoadLevel (Application.loadedLevel);
    }
}

不想认真看那几处修改位置的可以直接复制之后覆盖原来的GameManager脚本。

然后打开Enemy2D脚本,修改一处:

void EnemyDamaged(int damage){
        if (enemyHealth > 0) {
            enemyHealth -= damage;      
        }

        if (enemyHealth <= 0) {
            enemyHealth = 0;
            Destroy(gameObject);
            if(basicEnemy)
            {
                gameManager.curEXP += 5;
                GameManager.curCoins += 5;
            }
            if (advancedEnemy)
            {
                gameManager.curEXP += 10;
                GameManager.curCoins += 10;   
            }   
        }
    }

修改完成之后,我们打到basic级别的敌人就会得到五点经验,五块钱,advanced级别的敌人就是十点经验和十块钱。这样就比较像正常的RPG游戏了。

好了,这一讲没什么内容,我们马上看下一讲。

二十、开始我们的武器设置

其实视频里面这一讲是非常简单的,首先是调整一下原本的LevelSelect画面的尺寸,我就不唠叨了,直接贴出代码:

using UnityEngine;
using System.Collections;

public class LevelSelect : MonoBehaviour
{
    int sw = Screen.width;
    int sh = Screen.height;

    public string Level1;
    public string Level2;

    void OnGUI()
    {
        //进入第一关的按钮
        if (GUI.Button(new Rect(sw * .1f, sh * .2f, sw * .3f, sh * .5f), "第一关"))
        {
            Application.LoadLevel(Level1);
        }
        //判断是否进入第二关
        if (PlayerPrefs.GetInt("LevelUnlock", 0) >= 2)
        {
            if (GUI.Button(new Rect(sw * .6f, sh * .2f, sw * .3f, sh * .5f), "第二关"))
            {
                PlayerPrefs.DeleteKey("LevelUnlock");
                Application.LoadLevel(Level2);
            }
        }
        else
        {
            GUI.Box(new Rect(sw * .6f, sh * .2f, sw * .3f, sh * .5f), "\n\n\n未解锁\n" + "第二关");
        }
    }
}

(我用了VS 2013的自动排版,感觉挺不错的,这样看起来就整齐多了。其实看很多视频,mono也是可以自动排版的,但是我实在搞不懂是怎么实现那些功能的。)

然后就是截个图让大家看看调整之后的效果:


这样是不是整齐多了呢?

接着是对GameManager进行修改,由于下一节还要继续改,就暂时不放代码。我们先看看要改哪个部分。首先是pauseMenu,也就是暂停按钮的设置。

上面这个,在之前的p按钮里面稍微改下就可以的。我就不多说了。退出游戏的话,可以利用Application.LoadLevel("Mainmenu");回到主菜单,然后再在主菜单那里退出。或者也可以用Application.Quit();这个是直接退出游戏的,不推荐现在使用,因为这个无法在我们的unity里面测试,要测试这个指令就必须把游戏导出来,然后再去测试。现在知道有这么个东西就行了。

if (Input.GetKeyDown(KeyCode.P))
        {
            //pauseMenu = !pauseMenu;   
            pauseMenu = true;
            Time.timeScale = 0;
        }

input这里,增加一个Time.timeScale = 0;就可以让整个游戏画面禁止。当然,也包括我们的玩家。暂停游戏就是这么实现的。当这个数字为1时则是正常速度运行游戏,数值变小或者变大都会对我们整个游戏的运行产生影响。在弄这个玩意的时候我突然灵光一闪,想到了一点很有趣的东西。有时候我们需要实现一种这样的效果,让除了主角之外的全场变慢,这个是很多电影里面经常看到的东西。最笨的方法,就是把和主角在一起的全部互动物体通通减速,但是这样除非所有物体都标有相同的tag,否则很难实现。另一种方法就是利用这个Time.timeScale,比如说,我们可以让它等于0.5,那么就可以实现敌人慢动作的效果了。有人会说,你进水了呀,这样调整之后我们主角也慢动作了啊。可是,我们可以在这种特殊的情况下提高主角的速度嘛。如果只是针对主角一人的话,那就好办了。主角可以速度翻倍,甚至翻四倍,那就够了,嘿嘿。这样才能让全场的机关慢下来,哈哈哈哈。我在想,也许有些忍者游戏可以这样设计~~

好了,如果我们想要回到游戏,只需要让Time.timeScale的值重新等于1就可以了。也可以让它在一定时间内慢慢增加或者慢慢减少,来让我们的游戏达到某种突然变得松弛或紧张起来的感觉。

至于武器菜单,看下一讲吧:

二十一、简单的武器菜单

在这个游戏教程里面,并没有那种NPC式的商店或者是比较复杂的购物系统,所以,目前我们的金钱暂时没办法用来进行复杂的购物设计,只能用来做一些比较简单的解锁。首先我先将整个修改之后的GameManager脚本贴出来吧,贴出来之后我再来说明一下:

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour
{
    //引用Controller2D
    public Controller2D controller2D;
    //角色生命值
    public Texture playersHealthTexture;
    //控制上面那个Teture的屏幕所在位置
    public float screenPositionX;
    public float screenPositionY;
    //控制桌面图标的大小
    public int iconSizeX = 25;
    public int iconSizeY = 25;
    //初始生命值
    static public int curHealth = 3;
    //游戏刚开始时,第一级的最大生命值,以后每级增加1。
    public int maxHealth = 3;

    GameObject player;
    //我们的子弹的攻击力
    static public int bulletDamage = 1;
    //当前经验值
    public int curEXP;
    //从第一级升到下一级需要的经验值(以后每一级增加50)
    int maxEXP = 50;
    //游戏刚开始时的等级
    int level = 1;
    //游戏刚开始的金钱
    static public int curCoins = 0;

    //这个布尔值用于判定是否显示角色的状态
    bool playerStats;
    //下面这个用于显示角色的状态
    public GUIText statsDisplay;
    //用于暂停的布尔值
    bool pauseMenu;
    //用于判断是否是否保存
    int saved = 0;
    //自动保存计时器
    public float autoSaveTimer;
    //用于是否显示武器菜单
    bool weaponsMenu;
    //屏幕宽度
    int sw = Screen.width;
    //屏幕高度
    int sh = Screen.height;
    void Awake()
    {
        saved = PlayerPrefs.GetInt("Game Saved");
        if (saved == 1)
        {
            curEXP = PlayerPrefs.GetInt("Player EXP");
            level = PlayerPrefs.GetInt("Player Level");
            curCoins = PlayerPrefs.GetInt("Player Coins");
            maxEXP = level * 50;
            maxHealth = level + 2;
            curHealth = maxHealth;
        }
    }

    //这个地方定义了私有变量player作为一个GameObject,然后用下面的FindGameObjectWithTag获取它,这样的话,在下面的伤害判断时,就可以用player.renderer了。
    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
    }

    void Update()
    {
        //洗钱
        if(Input.GetKeyDown(KeyCode.M)){
            curCoins += 100;
        }
        //自动存档功能
        autoSaveTimer += Time.deltaTime;
        if (autoSaveTimer >= 10f)
        {
            SaveGame();
            print("保存啦~~");
            autoSaveTimer = 0;
        }

        if (Input.GetKeyDown(KeyCode.P))
        {
            //pauseMenu = !pauseMenu;   
            pauseMenu = true;
            Time.timeScale = 0;
        }

        if (curEXP >= maxEXP)
        {
            LevelUp();
        }

        if (Input.GetKeyDown(KeyCode.C))
        {
            playerStats = !playerStats;
        }

        if (Input.GetKeyDown(KeyCode.E))
        {
            curEXP += 10;
        }

        if (playerStats)
        {
            statsDisplay.text = "等级:" + level + "\n经验:" + curEXP + "/" + maxEXP + "\n攻击力:" + bulletDamage + "\n金钱" + curCoins;
        }
        else
        {
            statsDisplay.text = "";
        }

        if (curHealth > maxHealth)
        {
            curHealth = maxHealth;
        }
    }
    //这个函数用于角色升级
    void LevelUp()
    {
        curEXP = 0;
        maxEXP = maxEXP + 50;
        level++;

        //升级的功效
        maxHealth++;
    }


    //OnGUI函数最好不要出现多次,容易造成混乱,所以我把要展示的东西都整合在这个里面
    void OnGUI()
    {

        //控制角色生命值的心的显示
        for (int h = 0; h < curHealth; h++)
        {
            GUI.DrawTexture(new Rect(screenPositionX + (h * iconSizeX), screenPositionY, iconSizeX, iconSizeY), playersHealthTexture, ScaleMode.ScaleToFit, true, 0);
        }

        if (pauseMenu)
        {
            //武器菜单按钮
            if (GUI.Button(new Rect(Screen.width * .25f, Screen.height * .3f, Screen.width * .5f, Screen.height * .1f), "武器菜单"))
            {
                pauseMenu = false;
                weaponsMenu = true;
            }

            //保存游戏按钮
            if (GUI.Button(new Rect(Screen.width * .25f, Screen.height * .4f, Screen.width * .5f, Screen.height * .1f), "保存游戏"))
            {
                SaveGame();
            }
            //“显示保存的数据”按钮
            //if (GUI.Button(new Rect(Screen.width * .25f, Screen.height * .6f, Screen.width * .5f, Screen.height * .1f), "显示保存的数据"))
            //{
            //    print("显示保存的数据");
            //    print("当前等级:" + PlayerPrefs.GetInt("Player Level"));
            //    print("当前经验:" + PlayerPrefs.GetInt("Player EXP"));
            //    print("是否保存:" + PlayerPrefs.GetInt("Game Saved"));
            //}

            //回到游戏按钮
            if (GUI.Button(new Rect(Screen.width * .25f, Screen.height * .5f, Screen.width * .5f, Screen.height * .1f), "回到游戏"))
            {
                Time.timeScale = 1;
                pauseMenu = false;
            }
            //退出游戏按钮
            if (GUI.Button(new Rect(Screen.width * .25f, Screen.height * .6f, Screen.width * .5f, Screen.height * .1f), "退出游戏"))
            {
                Application.LoadLevel("Mainmenu");
                //Application.Quit();
            }
        }

        if (weaponsMenu)
        {
            //第一武器
            if (PlayerPrefs.GetInt("Weapon1", 0) >= 1)
            {
                bulletDamage = 3;
                if (GUI.Button(new Rect(sw * .1f, sh * .2f, sw * .3f, sh * .5f), "已装备武器1"))
                {
                    bulletDamage = 3;
                }
            }
            else
            {
                if (GUI.Button(new Rect(sw * .1f, sh * .2f, sw * .3f, sh * .5f), "购买武器1"+"\n价格:200金币"))
                {
                    if (curCoins >= 200) 
                    {
                        curCoins -= 200;
                        PlayerPrefs.SetInt("Weapon1", 1);
                    }
                }
            }
            //第二武器
            if (PlayerPrefs.GetInt("Weapon2", 0) >= 1)
            {
                bulletDamage = 6;
                if (GUI.Button(new Rect(sw * .6f, sh * .2f, sw * .3f, sh * .5f), "已装备武器2"))
                {
                    bulletDamage = 6;
                }
            }
            else
            {
                if (GUI.Button(new Rect(sw * .6f, sh * .2f, sw * .3f, sh * .5f), "购买武器2" + "\n价格:500金币"))
                {
                    if (curCoins >= 500)
                    {
                        curCoins -= 500;
                        PlayerPrefs.SetInt("Weapon2", 1);
                    }
                }
            }
            //回到游戏按钮
            if (GUI.Button(new Rect(Screen.width * .25f, Screen.height * .5f, Screen.width * .5f, Screen.height * .1f), "回到游戏"))
            {
                Time.timeScale = 1;
                weaponsMenu = false;
            }
        }
    }

    public void SaveGame()
    {
        saved = 1;
        PlayerPrefs.SetInt("Player Level", level);
        PlayerPrefs.SetInt("Player EXP", curEXP);
        PlayerPrefs.SetInt("Player Coins", curCoins);
        PlayerPrefs.SetInt("Game Saved", saved);
        PlayerPrefs.SetInt("Player Damage", bulletDamage);
        print("已保存");
    }


    void PlayerDamaged(int damage)
    {   //此处使用player.renderer.enabled来进行判断,如果角色没有在闪烁,也就是存在的状态为真,那么才会受到伤害,这样可以避免角色连续受伤,还有另外一种方法是采用计时,这里没有采用那种方法。
        if (player.renderer.enabled)
        {
            if (curHealth > 0)
            {
                curHealth -= damage;
            }

            if (curHealth <= 0)
            {
                RestartScene();
            }
        }
    }

    void RestartScene()
    {
        Application.LoadLevel(Application.loadedLevel);
    }
}

内容很长,所以有兴趣的要耐心地好好看,虽然增加的内容也并不是很多。哈哈。我用VS 2013进行了自动排版操作,所以看起来整齐多了。mono的排版并不是很给力,所以我并不是很喜欢。首先我们增加了一个参数。bool weaponsMenu;用于判定是否显示武器菜单,这个在武器菜单的GUI中用到的。接着呢,我们做了个这样的小东西:

//洗钱
        if(Input.GetKeyDown(KeyCode.M)){
            curCoins += 100;
        }

这个只是我在测试过程中用于快速增加金钱,不用慢慢去打怪收钱就可以马上购买装备的一个测试而已,好孩纸不要研究怎么作弊哈。接着呢,就在GUI这一块做了点处理,并且我已经设计好了,购买了武器1的话,我们的bulletDamage(也就是攻击力,因为在目前来说,我们只有一种武器,没有一堆武器)就会变成3,购买了武器2的话,我们的bulletDamage就会变成6,不过有个小问题我还没处理的,就是购买了武器2之后,理论上来说还是可以购买武器1的,而且购买了武器1之后,理应还保持武器2的战斗力(除非进行武器的切换)不过这个是个小问题,大家可以用一些布尔值来解决,我就不再赘述了。至于武器的切换,以及武器在场景中的实际发挥(尤其是近战武器),这个是以后的研究重点,这一篇就暂时先不说了。

其实前面提到的关卡解锁和这次提到的所谓购买武器,并没有什么本质上的区别。武器的购买系统并没有真正的做出来,武器的合成、锤炼、优化等等这方面的内容那就更不用说了。所以,目前只能算是个非常非常简单的2D RPG的雏形,但是这几天的总结和学习也确实让我掌握了很多很多的东西。我觉得,搞开发的人确实很有必要写博客。因为有些东西只有写出来才不会忘记,也才能加深印象。自己稍微懂了和能够用文字表述出来是两个完全不同的档次,还是有很大区别的。所以,我建议有学习Unity的朋友也一起写写博客,有空多多交流吧。

二十二、结束场景

这是本篇中的最后一讲了。下一篇博客暂时不会继续讲2D RPG的内容。好了,我们继续看内容,这一篇只是在炒冷饭,并没有增加什么新的知识点。就是制作一个游戏结尾的场景而已。

我们建一个新的场景,命名Endscene,然后命名一个叫做EndScene的脚本,扔到这个场景的摄像机上面去,进行以下编辑:

using UnityEngine;
using System.Collections;

public class EndScene : MonoBehaviour
{
    public Texture backgroundTexture;

    public float sPlaceX;
    public float sPlaceY;
    public float sSizeX;
    public float sSizeY;

    public string Level;

    void OnGUI()
    {
        GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), backgroundTexture);

        if (GUI.Button(new Rect(Screen.width * sPlaceX, Screen.height * sPlaceY, Screen.width * sSizeX, Screen.height * sSizeY), "回到主菜单"))
        {
            Application.LoadLevel(Level);
        }
    }
}

然后就随便找一张图片做背景(这个随你喜欢),然后在外面设置你要返回的那个Level的名字。这个嘛,我们就设置为Mainmenu就可以了。

接着我们在场景2里面随便建一个物体,让主角碰到它之后就会跳转到结束场景。这个比较简单,我自己就没弄了。有需要的再去弄一下。然后不要忘了,我们还要把这个叫做Endscene的场景拖拽到菜单栏的FileBuild Settings的场景里面去:


然后测试运行,基本没什么问题的,我就不截图了。

好啦,到这里,我们就已经实现了一个2D横版RPG游戏的许多基本功能,包括场景加载、简单的战斗计算、经验系统、游戏存档、跳跃、踏板、解锁装置、自动存档、武器系统等等。虽然说这些东西都比较简单,暂时做不出什么大型和炫酷的效果,而且既没有音效,也没有贴图和动画,非常单调。不过这些基本的要素掌握了之后,相信很多人可以利用发散性的想法设计出很多有趣的东西来了。我觉得看教程最大的意义是学习一种方法并借鉴别人的解决方案,而不是从头到尾照做一遍,那样的话还是没办法设计出属于自己的东西的。接下来的这几天可能稍有点忙,估计暂时不更新博客了。不过一有空我就会马上更新的。

这篇写了三天的博客就先到这里,如果大家有什么看的不太清楚的可以留言。欢迎大家一起学习,一起交流,以后我会尽量把一些好东西上传到自己的资源里面去。当然,我也会把这两天的教程的内容上传到我的CSDN资源里面共享给大家。需要注意的是必须使用4.3或者以上版本的Unity才能打开我的资源。这些资源将免费公开,不收取任何下载币,嘻嘻。


本次教程的工程文件下载: http://download.csdn.net/detail/sinolzeng/7279823




Unity中使用Rigidbody2D制作2D横版跳跃游戏时,控制玩家的精准移动和跳跃主要涉及到对玩家角色的Rigidbody2D组件进行编程操作。以下是一个基础的控制逻辑,它包括了水平移动和跳跃的基本实现: 1. 水平移动:通过检测玩家的输入(例如,使用左右箭头键或A/D键),你可以获取一个水平方向的移动值,并应用到Rigidbody2D的velocity属性上,以实现水平移动。例如: ```csharp void Update() { float move = Input.GetAxis("Horizontal"); // 获取水平轴上的输入 rb2d.velocity = new Vector2(move * speed, rb2d.velocity.y); // 设置水平速度 } ``` 2. 跳跃:为了实现跳跃,你需要检测跳跃键(例如空格键)的按下,并在条件满足的情况下给角色施加向上的力。你还需要判断角色是否站在地面上,这通常涉及到碰撞检测。以下是跳跃的简单实现: ```csharp public float jumpForce = 700f; // 跳跃力 public Transform groundCheck; // 地面检测点的位置 public float checkRadius = 0.3f; // 检测半径 public LayerMask whatIsGround; // 什么层被认为是地面 bool isGrounded; // 角色是否在地面上 void Update() { // 检测是否在地面 isGrounded = Physics2D.OverlapCircle(groundCheck.position, checkRadius, whatIsGround); if (isGrounded && Input.GetButtonDown("Jump")) // 如果玩家按下跳跃键且在地面 { rb2d.AddForce(new Vector2(0f, jumpForce)); // 向上施加力 } } ``` 3. 跳跃缓冲:为了使跳跃更加真实,可以加入一个短暂的时间窗口,在这个窗口内,即使玩家松开跳跃键,角色仍然可以继续上升,这被称为跳跃缓冲。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值