益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】

目录

      • 引言
  • 【小游戏】保护小猫 效果展示+完整项目源码
      • 游戏图片
      • 工程解析
      • 核心代码讲解
      • 接入声网音频SDK
    • 游戏源码下载
  • 总结
    • 评论区抽奖送书
      • 抽奖规则
      • 推荐理由(⭐⭐⭐⭐⭐)

益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第1张图片


引言

  • 小伙伴们好久不见,今天给大家带来一个保护小猫的小游戏源码分享
  • 完整项目源码在文末可以直接下载体验,也可以关注文末卡片获取更多源码!
  • 下面来对这个小游戏的工程源码,简单分析一下
  • 这里就不一步一步来演示啦,主要是分享源码~

【小游戏】保护小猫 效果展示+完整项目源码

先来看一下游戏效果

游戏效果展示:

核心玩法 就是使用鼠标控制一个方块来保护小猫不被障碍物碰到

小猫会不停的想上攀爬来计算分数,当碰到障碍物的时候比赛就会结束了!

非常考验玩家的反应和手速哦哈哈,反正我玩一会就GG了~


游戏图片

先来看几张游戏内的截图,包括开始时的画面,游戏中和游戏结束等画面

如下所示:
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第2张图片

益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第3张图片
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第4张图片
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第5张图片


工程解析

下面来看一下工程目录结构如下:
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第6张图片

  • _Prefabs:预制体
  • _Scenes:场景文件夹
  • _Scripts:放脚本的文件夹
  • _Sprites:放精灵图片的文件夹
  • Animations:动画控制器文件夹
  • Common:通用素材和脚本的文件夹
  • Music_Sound:存放音频的文件夹
  • Plugins:存放插件的文件夹
  • Resources:存放加载文件的文件夹

预制体的话主要就是游戏中的一些UI元素,因为这是一个2D游戏,所以UI中用到的素材、预制体等都是UI了

场景声网话只有一个主场景,因为游戏很简单,所以包括开始界面和游戏界面都是在同一个场景中完成的!

脚本的话有十来个,控制游戏的一个运行逻辑过程!

精灵图片有很多,会在游戏过程进行不断的切换达到一个视觉效果~
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第7张图片益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第8张图片

还有就是音频文件夹了,主要是有一个背景音乐、一个开始和结束的音效
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第9张图片

Resources文件夹中的东西比较重要,因为我们在玩这个小游戏的时候,一个核心的玩法就是场景是随机进行变化处理的

这里是预先做好了很多个预制体模型,用于在游戏过程中随机进行生成达到一个增加游戏可玩性的效果

这也是这个小游戏最核心的可玩性机制了,如果每次刷新的障碍物都是一样的,那么这个小游戏确实也没啥好玩的哈哈~
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第10张图片
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第11张图片


核心代码讲解

这个工程有两个游戏控制器Controller,分别是 MainControllerHomeController

HomeController中的内容比较简单

MainController是控制游戏运行的一个核心控制器,控制核心的障碍的加载方法、背景图片的切换和加载

以及新关卡加载、开始、暂停、继续、结束游戏的逻辑处理和UI的实时更新等功能

是整个游戏运行的核心脚本,部分代码如下:

public class MainController : BaseController {
     
    public Transform backRegion, spawnRegion;
    public BackItem backPrefab;
    public List<GroupObstacle> obstaclePrefabs;
    private int colorIndex = 0, backCount = 0;
    private BackItem lastBack = null;
    public enum GameState {
     START, LOADED, PLAYING, GAME_OVER, COMPLETED, GUIDE, SHOP, PAUSED};
    public static GameState gameState = GameState.START, lastState = GameState.START;
    public GameObject startFrame, guideFrame, shopFrame, gameTitle;
    public Protection protection;
    public ChallengeController challengeController;
    public ClassicController classicController;
    public static MainController instance;
    public ContinueFrame continueFrame;
    public MyPlayer player;
    private int spawnLevel = 1, passedLevel = 0, currentLevel = 0;
    private int savedObstacleIndex, lastObstacleIndex;

    public Animator flash;
    
    private List<BackItem> backs = new List<BackItem>();

    protected override void Awake()
    {
     
        base.Awake();
        instance = this;
    }

    protected override void Start()
    {
     
        base.Start();
        SetupBackground();
        LoadAllObstacles();
    }

    private void LoadAllObstacles()
    {
     
        obstaclePrefabs = Resources.LoadAll<GroupObstacle>("GroupObstacles").ToList();
        obstaclePrefabs.Sort((x, y) => Utils.GetGroupObIndex(x.name).CompareTo(Utils.GetGroupObIndex(y.name)));
    }

    private void SetupBackground()
    {
     
        lastBack = SpawnNewBackground(Vector3.zero);        
        lastBack.SetUp(0, 5, true, true, 0);
        backs.Add(lastBack);
        colorIndex = Random.Range(1, 5);
        float canvasHeight = backRegion.parent.GetComponent<RectTransform>().sizeDelta.y;
        for (int i = 0; i < 2; i++)
        {
     
            Vector3 pos = new Vector3(0, lastBack.rect.anchoredPosition.y + (i == 0 ? canvasHeight : Const.BACK_HEIGHT));
            lastBack = SpawnNewBackground(pos);
            lastBack.SetUp(colorIndex, colorIndex, i == 0, false, 1);
            backs.Add(lastBack);            
        }
    }
 }

MyPlayer脚本是挂载到我们的小猫咪身上的,主要是控制 图标的切换 以及 猫咪身上的碰撞体 相关的逻辑

在碰到障碍物的时候触发游戏结束的事件,进行相关逻辑的处理!

    public SpriteRenderer icon;
    public Sprite[] iconSprites;
    public GameObject playerIconPrefab;
    public GameObject bubble;
    public GameObject[] bubbleParts;

    void Start()
    {
     
        UpdateSprite();
    }

    public void UpdateSprite()
    {
     
        int selectedType = CUtils.GetPlayerType();
        icon.sprite = iconSprites[selectedType];
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
     
        if (collision.collider.tag == "Obstacle" && MainController.IsPlaying())
        {
     
            MainController.instance.CollideWithObstacle();
            MainController.instance.CheckAndShowContinue();
            icon.gameObject.SetActive(false);
            GetComponent<CircleCollider2D>().isTrigger = true;
            bubble.SetActive(false);
            foreach (GameObject p in bubbleParts)
            {
     
                p.SetActive(true);
            }
            GetComponent<Animator>().SetTrigger("Break");
            Sound.instance.Play(Sound.Others.Break);
            Timer.Schedule(this, 0.5f, () =>
            {
     
                Sound.instance.Play(Sound.Others.Die);
            });
            SpawnIcon();
        }
    }    

    private void SpawnIcon()
    {
     
        GameObject obj = (GameObject)Instantiate(playerIconPrefab);
        obj.transform.localPosition = icon.transform.position;
        obj.GetComponent< SpriteRenderer>().sprite = iconSprites[CUtils.GetPlayerType()];
        obj.transform.localScale = Vector3.one * 0.5f;
    }

    public void Reset()
    {
     
        icon.gameObject.SetActive(true);
        GetComponent<CircleCollider2D>().isTrigger = false;
        bubble.SetActive(true);
        foreach(GameObject p in bubbleParts)
        {
     
            p.SetActive(false);
        }
    }

其他的代码就不一一讲解了。喜欢这个小游戏的可以下载源码工程娱乐一下

虽然是个小游戏,总共有十几个脚本组成,但是还是挺有可玩性的!


接入声网音频SDK

小游戏的核心玩法解决了,下面我们再给他加上声网的音频SDK实现一个语音聊天的功能!

接入代码非常简单,只需要去声网下载音频通话的SDK,并创建项目获取 APP ID 就好了!

这是下载SDK的地方:https://docs.agora.io/cn/All/downloads?platform=Unity

然后我们找到Unity的音频SDK进行下载
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第12张图片
然后去 控制台 创建一个项目
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第13张图片直接选择调试模式,创建成功后,点击 将 APP ID复制出来,后面会用到!

现在我们已经拿到声网音频的SDK和项目的 APP ID 了,接下来就是把这个SDK接入到我们的小游戏中去~

接入进去的方法也很简单,我们把下载的SDK直接复制到我们的小游戏工程中去!

然后添加语音聊天的功能即可,核心代码如下:

using UnityEngine;
using UnityEngine.UI;
#if(UNITY_2018_3_OR_NEWER)
using UnityEngine.Android;
#endif
using agora_gaming_rtc;

public class HelloUnity3D : MonoBehaviour
{
     
    private InputField mChannelNameInputField;//频道号
    public Text mShownMessage;//提示
    private Text versionText;//版本号
    public Button joinChannel;//加入房间
    public Button leaveChannel;//离开房间
    private Button muteButton;//静音

    private IRtcEngine mRtcEngine = null;

    // 输入App ID后,在App ID外删除##
    [SerializeField]
    private string AppID = "app_id";

    void Awake()
    {
     
        QualitySettings.vSyncCount = 0;
        Application.targetFrameRate = 30;
        //muteButton.enabled = false;
        CheckAppId();
    }

    // 进行初始化
    void Start()
    {
     
#if (UNITY_2018_3_OR_NEWER)
        // 判断是否有麦克风权限,没有权限的话主动申请权限
			if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
			{
     
            Permission.RequestUserPermission(Permission.Microphone);
            } 

#endif
        joinChannel.onClick.AddListener(JoinChannel);
        leaveChannel.onClick.AddListener(LeaveChannel);
        //muteButton.onClick.AddListener(MuteButtonTapped);

        mRtcEngine = IRtcEngine.GetEngine(AppID);
        //versionText.GetComponent().text = "Version : " + getSdkVersion();

        //加入频道成功后的回调
        mRtcEngine.OnJoinChannelSuccess += (string channelName, uint uid, int elapsed) =>
        {
     
            string joinSuccessMessage = string.Format("加入频道 回调 uid: {0}, channel: {1}, version: {2}", uid, channelName, getSdkVersion());
            Debug.Log(joinSuccessMessage);
            mShownMessage.GetComponent<Text>().text = (joinSuccessMessage);
            //muteButton.enabled = true;
        };

        //离开频道回调。 
        mRtcEngine.OnLeaveChannel += (RtcStats stats) =>
        {
     
            string leaveChannelMessage = string.Format("离开频道回调时间 {0}, tx: {1}, rx: {2}, tx kbps: {3}, rx kbps: {4}", stats.duration, stats.txBytes, stats.rxBytes, stats.txKBitRate, stats.rxKBitRate);
            Debug.Log(leaveChannelMessage);
            mShownMessage.GetComponent<Text>().text = (leaveChannelMessage);
            //muteButton.enabled = false;
            // 重置静音键状态
            //if (isMuted)
           // {
     
          //      MuteButtonTapped();
            //}
        };

        //远端用户加入当前频道回调。 
        mRtcEngine.OnUserJoined += (uint uid, int elapsed) =>
        {
     
            string userJoinedMessage = string.Format("远端用户加入当前频道回调 uid {0} {1}", uid, elapsed);
            Debug.Log(userJoinedMessage);
            mShownMessage.GetComponent<Text>().text = (userJoinedMessage);
        };

        //远端用户离开当前频道回调。 
        mRtcEngine.OnUserOffline += (uint uid, USER_OFFLINE_REASON reason) =>
        {
     
            string userOfflineMessage = string.Format("远端用户离开当前频道回调 uid {0} {1}", uid, reason);
            Debug.Log(userOfflineMessage);
            mShownMessage.GetComponent<Text>().text = (userOfflineMessage);
        };

        // 	用户音量提示回调。 
        mRtcEngine.OnVolumeIndication += (AudioVolumeInfo[] speakers, int speakerNumber, int totalVolume) =>
        {
     
            if (speakerNumber == 0 || speakers == null)
            {
     
                Debug.Log(string.Format("本地用户音量提示回调   {0}", totalVolume));
            }

            for (int idx = 0; idx < speakerNumber; idx++)
            {
     
                string volumeIndicationMessage = string.Format("{0} onVolumeIndication {1} {2}", speakerNumber, speakers[idx].uid, speakers[idx].volume);
                Debug.Log(volumeIndicationMessage);
            }
        };

        //用户静音提示回调
        mRtcEngine.OnUserMutedAudio += (uint uid, bool muted) =>
        {
     
            string userMutedMessage = string.Format("用户静音提示回调 uid {0} {1}", uid, muted);
            Debug.Log(userMutedMessage);
            mShownMessage.GetComponent<Text>().text = (userMutedMessage);
        };

        //发生警告回调
        mRtcEngine.OnWarning += (int warn, string msg) =>
        {
     
            string description = IRtcEngine.GetErrorDescription(warn);
            string warningMessage = string.Format("发生警告回调 {0} {1} {2}", warn, msg, description);
            Debug.Log(warningMessage);
        };

        //发生错误回调
        mRtcEngine.OnError += (int error, string msg) =>
        {
     
            string description = IRtcEngine.GetErrorDescription(error);
            string errorMessage = string.Format("发生错误回调 {0} {1} {2}", error, msg, description);
            Debug.Log(errorMessage);
        };

        // 当前通话统计回调,每两秒触发一次。
        mRtcEngine.OnRtcStats += (RtcStats stats) =>
        {
     
            string rtcStatsMessage = string.Format("onRtcStats callback duration {0}, tx: {1}, rx: {2}, tx kbps: {3}, rx kbps: {4}, tx(a) kbps: {5}, rx(a) kbps: {6} users {7}",
                stats.duration, stats.txBytes, stats.rxBytes, stats.txKBitRate, stats.rxKBitRate, stats.txAudioKBitRate, stats.rxAudioKBitRate, stats.userCount);
            //Debug.Log(rtcStatsMessage);

            int lengthOfMixingFile = mRtcEngine.GetAudioMixingDuration();
            int currentTs = mRtcEngine.GetAudioMixingCurrentPosition();

            string mixingMessage = string.Format("Mixing File Meta {0}, {1}", lengthOfMixingFile, currentTs);
            //Debug.Log(mixingMessage);
        };

        //语音路由已发生变化回调。(只在移动平台生效)
        mRtcEngine.OnAudioRouteChanged += (AUDIO_ROUTE route) =>
        {
     
            string routeMessage = string.Format("onAudioRouteChanged {0}", route);
            Debug.Log(routeMessage);
        };

        //Token 过期回调
        mRtcEngine.OnRequestToken += () =>
        {
     
            string requestKeyMessage = string.Format("OnRequestToken");
            Debug.Log(requestKeyMessage);
        };

        // 网络中断回调(建立成功后才会触发)
        mRtcEngine.OnConnectionInterrupted += () =>
        {
     
            string interruptedMessage = string.Format("OnConnectionInterrupted");
            Debug.Log(interruptedMessage);
        };

        // 网络连接丢失回调
        mRtcEngine.OnConnectionLost += () =>
        {
     
            string lostMessage = string.Format("OnConnectionLost");
            Debug.Log(lostMessage);
        };

        // 设置 Log 级别
        mRtcEngine.SetLogFilter(LOG_FILTER.INFO);

        // 1.设置为自由说话模式,常用于一对一或者群聊
        mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_COMMUNICATION);

        //2.设置为直播模式,适用于聊天室或交互式视频流等场景。
        //mRtcEngine.SetChannelProfile (CHANNEL_PROFILE.CHANNEL_PROFILE_LIVE_BROADCASTING);

        //3.设置为游戏模式。这个配置文件使用较低比特率的编解码器,消耗更少的电力。适用于所有游戏玩家都可以自由交谈的游戏场景。
        //mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_GAME);

        //设置直播场景下的用户角色。 
        //mRtcEngine.SetClientRole (CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
    }

    private void CheckAppId()
    {
     
        Debug.Assert(AppID.Length > 10, "请先在Game Controller对象上填写你的AppId。.");
        GameObject go = GameObject.Find("AppIDText");
        if (go != null)
        {
     
            Text appIDText = go.GetComponent<Text>();
            if (appIDText != null)
            {
     
                if (string.IsNullOrEmpty(AppID))
                {
     
                    appIDText.text = "AppID: " + "UNDEFINED!";
                    appIDText.color = Color.red;
                }
                else
                {
     
                    appIDText.text = "AppID: " + AppID.Substring(0, 4) + "********" + AppID.Substring(AppID.Length - 4, 4);
                }
            }
        }
    }

    /// 
    /// 加入频道
    /// 
    public void JoinChannel()
    {
     
        // 从界面的输入框获取频道名称
        string channelName = "adc666";

        // 从界面的输入框获取频道名称
        //string channelNameOld = mChannelNameInputField.text.Trim();

        Debug.Log(string.Format("从界面的输入框获取频道名称 {0}", channelName));

        if (string.IsNullOrEmpty(channelName))
        {
     
            return;
        }
        // 加入频道
        // channelKey: 动态秘钥,我们最开始没有选择 Token 模式,这里就可以传入 null;否则需要传入服务器生成的 Token
        // channelName: 频道名称
        // info: 开发者附带信息(非必要),不会传递给频道内其他用户
        // uid: 用户ID,0 为自动分配
        mRtcEngine.JoinChannelByKey(channelKey: null, channelName: channelName, info: "extra", uid: 0);

        //加入频道并设置发布和订阅状态。 
        //mRtcEngine.JoinChannel(channelName, "extra", 0);
    }

    /// 
    ///  离开频道
    /// 
    public void LeaveChannel()
    {
     
        // 离开频道
        mRtcEngine.LeaveChannel();

        string channelName = "abc666";
        Debug.Log(string.Format("left channel name {0}", channelName));
    }

    void OnApplicationQuit()
    {
     
        if (mRtcEngine != null)
        {
     
            // 销毁 IRtcEngine
            IRtcEngine.Destroy();
        }
    }

    /// 
    /// 查询 SDK 版本号。 
    /// 
    /// 
    public string getSdkVersion()
    {
     
        string ver = IRtcEngine.GetSdkVersion();
        return ver;
    }


    bool isMuted = false;
    void MuteButtonTapped()
    {
     
        //设置静音或者取消静音
        string labeltext = isMuted ? "静音" : "取消静音";
        Text label = muteButton.GetComponentInChildren<Text>();

        if (label != null)
        {
     
            label.text = labeltext;
        }
        isMuted = !isMuted;
        // 设置静音(停止推送本地音频)
        mRtcEngine.EnableLocalAudio(!isMuted);
        Debug.Log("静音方法执行完成");
    }
}

只需要将这个脚本代码挂载到我们的场景中,然后加两个Button用于控制加入语音频道和离开语音频道即可

然后把我们在声网创建的项目APP ID 赋值上去就可以正常使用了!

本文接入音频SDK的部分写的不是特别详细,也可以单独去看怎样接入声网SDK的文章,专门学习一下!


游戏源码下载

文中这个 保护小猫的源码工程下载链接就在这里啦!

【保护小猫】 小游戏下载

有积分的小伙伴可以下载体验,积分不够的直接关注文章最下面的公众号回复:游戏资源

就可以领取几十款游戏源码和素材啦,看看有没有你想要的那一款!


总结

  • 本篇文章分享了一个益智类小游戏【保护小猫】的游戏源码并简单分析了一下游戏结构和代码部分
  • 并结合声网SDK实现了一个简单的语音聊天功能,虽然接入功能没有实现的很完整
  • 但是还是能满足我们的一个语音聊天的功能!
  • 后续有时间会针对声网SDK尝试做更多的功能来玩一下~ 功能强大

评论区抽奖送书

最后在评论区进行抽取三名幸运的小伙伴送下面这本书籍

  • 这次博主多方联系,最终在清华出版社搞到了一些unity 相关的书籍,在这里搞个小活动抽奖送给大家,对看书感兴趣的小伙伴可以参加一波呀,刚开始可能参与的不多,抽中概率很大哦!

  • 《C#实践入门(快捷学习C#编程和Unity游戏开发第4版.NET开发经典名著) 》

  • 一本非常经典的C#编程名著,结合Unity开发使用效果卓佳!

  • 尤其是想从事Unity开发刚入门的小伙伴,没必要去买专门的C#书籍,这本书就可以让你了解Unity用到的C#相关知识,对症下药可以极大地提高学习效率!

抽奖规则

规则如下:

规则如下
  • 给本篇博客文章 点赞 收藏 评论 三连,然后就可以在博客文章评论区抽奖送一本Unity的书籍!

  • 总共抽三本,中的几率还是很大的哦~ 想看书的小伙伴参与起来!

  • 中奖信息明天下午本篇文章评论区公布!记得留意呀!

  • 没抽到的,但是喜欢这本书的小伙伴也可以在网上自行购买哈,官方正品商店购买即可!

规则如上

推荐理由(⭐⭐⭐⭐⭐)

  •  在如今的技术环境中学习编程无疑是一项艰巨的任务,尤其是当面对众多可选的编程语言时。幸运的是,《C#实践入门(快捷学习C#编程和Unity游戏开发第4版.NET开发经典名著) 》消除了充满不确定性的学习过程,为初学者指明了一条成为有竞争力的Unity程序员的捷径。

  •  在本书中,你将首先学习编程的基本构成要素,涵盖从变量、方法、条件语句到类、面向对象编程在内的所有知识。打好基础后,你将开始探索Unity接口并创建C#脚本,进而将学到的知识转换为简单的游戏机制。

  •  在整个学习过程中,你将掌握编程方面的最佳实践以及宏观的架构知识,例如游戏管理器与灵活的应用程序架构。等到本书结束时,你将对C#的一些中级主题了然于胸,例如泛型、委托、事件等,从而在自己的项目中加以灵活运用。

书本图片如下,喜欢的小伙伴参与起来!
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第14张图片
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第15张图片
益智类小游戏【保护小猫】做一个暖心的铲屎官,还有语音聊天功能【文末送书】_第16张图片

你可能感兴趣的:(⭐️游戏制作⭐️,unity,益智类,声网,语音聊天)