Suck the cat with code! This article is participating in [Cat essay Activity]


🍕 introduction

  • I have one for you todayTo protect the kittenSmall game source sharing
  • Complete project source codeAt the end of the article can be directly downloaded to experience!
  • The following to this small game project source, a simple analysis, here is not step by step to demonstrate, mainly to share the source ~
  • Plus theSoundnet's audio SDKVoice chat function, also very practical oh ~

🎄 [small game] protect the cat effect display + complete project source code

Let’s take a look at the game effect first

Game effect display:

The core gameplay is to use the mouse to control a block to protect the kitten from obstacles

The kitten will keep climbing to score points, and the race will be over when it hits an obstacle!

Very test player reaction and hand speed oh ha ha, anyway I play for a while GG ~


😻 Game pictures

Let’s take a look at some in-game screenshots from the beginning of the game, during and after the game

As follows:


😺 Project analysis

The project directory structure is as follows:

  • _Prefabs: indicates the prefab
  • _Scenes: Scene folder
  • _Scripts: folder for storing scripts
  • _Sprites: folder for Sprite images
  • Animations: Animation controller folder
  • Common: Folder for Common materials and scripts
  • Music_Sound: Folder for storing audio
  • Plugins: folders where plug-ins are stored
  • Resources: folder for loading files

Preforms are mainly UI elements in the game, because this is a 2D game, so the materials used in the UI, preforms and so on are UI

There is only one main scene, because the game is very simple, so both the start screen and the game screen are completed in the same scene!

Script words have a dozen, control the game of a running logic process!

SpriteThere are many, in the course of the game will be constantly switching to achieve a visual effect ~

Also,Audio folderBasically, there is a background music, a start and end sound effects

What’s in the Resources folder is important because one of the core elements of the game we’re playing is that the scene is treated randomly

There are many prefabricated models that are randomly generated during the game to increase the gameplay

That’s what this little game is all aboutGameplay mechanicsIf the obstacles are the same every time, then this little game is really not fun haha ~


😹 core code explanation

This project has two game controllers, the MainController and the HomeController

The content in HomeController is relatively simple

The MainController is a core controller that controls how the game runs, how the core obstacles are loaded, how the background images are switched and loaded

As well as new level loading, start, pause, continue, end game logic processing and real-time UI updates and other functions

Is the core script of the entire game operation, part of the code is as follows:

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); }}}Copy the code

The MyPlayer script is mounted to our cat and controls the switch of ICONS and the logic associated with collisions on the cat

When encountering obstacles trigger the end of the game event, related logic processing!

    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.5 f, () => { 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.5 f;
    }

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

I won’t go through the rest of the code. Like this little game can download the source code project entertainment

Although it is a small game, a total of a dozen scripts, but still quite playable!


😼 Access the audio SDK of sonnet

The core gameplay of the small game is solved, and we will add the audio SDK to achieve a voice chat function for him!

The access code is very simple, just download the SDK of audio call on Sonnet, and create a project to get the APP ID!

This is the place where to download the SDK: docs. Agora. IO/cn/All/down…

And then we findUnity's audio SDKTo downloadThen go toThe consoleCreate a projectDirect selectionDebug mode, and click WillAPP IDCopy that, and we’ll use it later!

Now we have got the SDK of sonnet audio and the APP ID of the project. The next step is to add this SDK into our small game

Access to the method is also very simple, we download the SDK directly copy into our small game project!

This folder is directly copied into our small game project!

Then add voice chat function, the core code is as follows:

 private InputField mChannelNameInputField;/ / channel number
    public Text mShownMessage;/ / hint
    public Button joinChannel;// Join the room
    public Button leaveChannel;// Leave the room

    private IRtcEngine mRtcEngine = null;

    // After entering the App ID, delete ## outside the App ID
    [SerializeField]
    private string AppID = "app_id";

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

    // Initialize
    void Start()
    {
#if (UNITY_2018_3_OR_NEWER)
        // Check whether you have microphone permission. If not, apply for permission
			if(! Permission.HasUserAuthorizedPermission(Permission.Microphone)) { Permission.RequestUserPermission(Permission.Microphone); }#endif
        joinChannel.onClick.AddListener(JoinChannel);
        leaveChannel.onClick.AddListener(LeaveChannel);


        mRtcEngine = IRtcEngine.GetEngine(AppID);


        // Join channel callback after success
        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;
        };

        // Leave channel callback.
        mRtcEngine.OnLeaveChannel += (RtcStats stats) =>
        {
            string leaveChannelMessage = string.Format({0} "left channel callback time, 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);

        };

    /// <summary>
    ///Join channel
    /// </summary>
    public void JoinChannel()
    {
        // Get the channel name from the input box on the interface
        string channelName = "666666";

        // Get the channel name from the input box on the interface
        //string channelNameOld = mChannelNameInputField.text.Trim();

        Debug.Log(string.Format("Get channel name {0} from interface input box", channelName));

        if (string.IsNullOrEmpty(channelName))
        {
            return;
        }
        // Join the channel
        // channelKey: dynamic key, we did not select the Token mode, here can pass null; Otherwise, you need to pass in the tokens generated by the server
        // channelName: indicates the channelName
        // info: developer attached information (not necessary), not passed to other users in the channel
        // uid: indicates the user ID. 0 is automatically assigned
        mRtcEngine.JoinChannelByKey(channelKey: null, channelName: channelName, info: "extra", uid: 0);

        // Join the channel and set the publish and subscribe status.
        //mRtcEngine.JoinChannel(channelName, "extra", 0);
    }

    /// <summary>
    ///Leave the channel
    /// </summary>
    public void LeaveChannel()
    {
        // Leave the channel
        mRtcEngine.LeaveChannel();

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

    void OnApplicationQuit()
    {
        if(mRtcEngine ! =null)
        {
            / / destroy IRtcEngineIRtcEngine.Destroy(); }}Copy the code

Just mount the script code into our scenario and add two buttons to control joining and leaving the voice channel

Then assign the APP ID of the project we created in sonnet to it and it can be used normally!

The access to audio SDK part of this article is not particularly detailed, you can also go to see how to access the audio network SDK article, specifically learn about it!


🎁 game source download

In this article the protection of the cat source project download link is here!

【 Cat protection 】 small game download

Interested partners can download experience ~


💬 summary

  • This article shares a puzzle game [cat protection] game source code and a simple analysis of the game structure and code part

  • Combined with the SDK of sonnet, a simple voice chat function is realized, although the access function is not very complete

  • But still can satisfy our one voice chat function!

  • Later, I will try to make more functions for soundnet SDK to play ~ powerful