Suck the cat with code! This paper is participating in[Cat Essay Campaign]


📢 preface

  • I just got in touch with the voice call SDK of Sonnet two days ago and found it very interesting

  • That today will bring you a [dozen ground cat] small game

  • Along with access to sound network SDK, to achieve a world chat channel function!


🎬 [dozen ground cat] small game

This small game is actually very simple, three scripts done, or very interesting!

Kittens jump out at random, and then we bash kittens with hammers haha

And then there is a world chat channel, using sound net SDK implementation, is also very simple, the article is introduced how to access

Check it out! Take a look at the renderings:

Let’s talk about the steps of making this small game!


1️ retail: setting up scene and model configuration

This step is very simple, first build a random scene

Because it is playing ground cat, so add a few pit to let the small cat jump out to let us play!

Then add a hammer on the top, when we click on the mouse to hit it!

Then hide the kitten under the pit and write a script for him to jump out for us to fight


2️ one: write scripts and let cats jump out automatically

The code is very simple, first declare several states of the cat, such as: under, above, hit and so on

Then we write a decision condition that makes the cat do different things in different states!

The core code is as follows:

	enum State{
		UNDER_GROUND,
		UP,
		ON_GROUND,
		DOWN,
		HIT,
	}
	State state;

void Update () 
	{

		if (this.state == State.UP) 
		{
			transform.Translate (0.this.moveSpeed, 0);

			if (transform.position.y > TOP) 
			{
				transform.position = 
					new Vector3 (transform.position.x, TOP, transform.position.z);

				this.state = State.ON_GROUND;

				this.tmpTime = 0; }}else if (this.state == State.ON_GROUND)
		{
			this.tmpTime += Time.deltaTime;

			if (this.tmpTime > this.waitTime) 
			{
				this.state = State.DOWN; }}else if (this.state == State.DOWN) 
		{
			transform.Translate (0, -this.moveSpeed, 0);

			if (transform.position.y < BOTTOM) 
			{
				transform.position = 
					new Vector3(transform.position.x, BOTTOM, transform.position.z);

				this.state = State.UNDER_GROUND; }}}Copy the code

So our little cat can show different effects according to different states!


3️ discount: mouse click on the code

Because we do the effect of the mouse click, the hammer will hit where so here we add a script to the hammer, let him click the mouse on the time to follow the coordinates of the mouse to hit!

The core code is as follows:

void Update () 
	{
		if(Input.GetMouseButtonDown(0))
		{
			Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
			RaycastHit hit;

			if (Physics.Raycast(ray, out hit, 100)) 
			{
				GameObject mole = hit.collider.gameObject;
					
				bool isHit = mole.GetComponent<MoleController> ().Hit ();

				// if hit the mole, show hummer and effect
				if (isHit) 
				{
					StartCoroutine (Hit (mole.transform.position));

					ScoreManager.score += 10; }}}}Copy the code

The 4th ️, add a random script to let the cat jump out

We wrote in the first step that kittens do different things in different states

You also need to write a script to control the cat’s random jump, which will make the game more playable!

The core code is as follows:

IEnumerator Generate()
	{
		this.generate = true;

		while (this.generate) 
		{
			// wait to generate next group
			yield return new WaitForSeconds (1.0 f);

			int n = moles.Count;
			int maxNum = (int)this.maxMoles.Evaluate ( GameManager.time );

			for (int i = 0; i < maxNum; i++) 
			{
				// select mole to up
				this.moles [Random.Range (0, n)].Up ();
								
				yield return new WaitForSeconds (0.3 f); }}}Copy the code

So our little game is done! But soundnet’s SDK does not yet feature a world chat channel

So keep reading!


5️ download audio call SDK on Cosnet and create a project to obtain APP ID

The address of sound net is below, if you don’t know, you can simply know ~

Sonnet official website: www.agora.io/cn/communit…

First go in after the need to register and so on here will not say, very simple ~

And then find a place to download the SDK: docs. Agora. IO/cn/All/down…

Sonnet supports a lot of platforms, anyway I know the platform and sonnet have cooperation, have to say sonnet is very powerful!

And then we findUnity's audio SDKTo downloadDrop in when you download the SDKThe consoleCreate a projectSince we use it for testing, we can choose debug mode directly. Please refer to the documentation on the official website to see the difference between the two:Docs. Agora. IO/cn/agora % 20…

After the creation is successful, clickAPP IDButton, copy it, it will be used later in the code!


6️ one: integrate the audio SDK of soundnet into the project

In the previous step, we put together the audio SDK, which includes a sample project

Convenient for us to access when you can learn!

Here we just put the downloaded SDK directly into our project

Then we create a new script to manage the World chat channel!

The core code is as follows:

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;/ / channel number
    public Text mShownMessage;/ / hint
    private Text versionText;/ / version number
    public Button joinChannel;// Join the room
    public Button leaveChannel;// Leave the room
    private Button muteButton;/ / mute

    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;
        //muteButton.enabled = false;
        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);
        //muteButton.onClick.AddListener(MuteButtonTapped);

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

        // 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);
            //muteButton.enabled = false;
            // Reset the mute button status
            //if (isMuted)
           / / {
          // MuteButtonTapped();
            / /}
        };

        // Remote user joins current channel callback.
        mRtcEngine.OnUserJoined += (uint uid, int elapsed) =>
        {
            string userJoinedMessage = string.Format("Remote user joins current channel callback uid {0} {1}", uid, elapsed);
            Debug.Log(userJoinedMessage);
            mShownMessage.GetComponent<Text>().text = (userJoinedMessage);
        };

        // Remote user leaving the current channel callback.
        mRtcEngine.OnUserOffline += (uint uid, USER_OFFLINE_REASON reason) =>
        {
            string userOfflineMessage = string.Format("Remote user leaves current channel callback uid {0} {1}", uid, reason);
            Debug.Log(userOfflineMessage);
            mShownMessage.GetComponent<Text>().text = (userOfflineMessage);
        };

        // User volume prompt callback.
        mRtcEngine.OnVolumeIndication += (AudioVolumeInfo[] speakers, int speakerNumber, int totalVolume) =>
        {
            if (speakerNumber == 0 || speakers == null)
            {
                Debug.Log(string.Format("Local user volume prompt callback {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); }};// User mute prompt callback
        mRtcEngine.OnUserMutedAudio += (uint uid, bool muted) =>
        {
            string userMutedMessage = string.Format("User mute prompt callback uid {0} {1}, uid, muted);
            Debug.Log(userMutedMessage);
            mShownMessage.GetComponent<Text>().text = (userMutedMessage);
        };

        // A warning callback occurred
        mRtcEngine.OnWarning += (int warn, string msg) =>
        {
            string description = IRtcEngine.GetErrorDescription(warn);
            string warningMessage = string.Format("Warning callback {0} {1} {2} occurred", warn, msg, description);
            Debug.Log(warningMessage);
        };

        // An error callback occurred
        mRtcEngine.OnError += (int error, string msg) =>
        {
            string description = IRtcEngine.GetErrorDescription(error);
            string errorMessage = string.Format("Error callback {0} {1} {2}", error, msg, description);
            Debug.Log(errorMessage);
        };

        // Call statistics callback is triggered every two seconds.
        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);
        };

        // The voice route has changed. (Mobile only)
        mRtcEngine.OnAudioRouteChanged += (AUDIO_ROUTE route) =>
        {
            string routeMessage = string.Format("onAudioRouteChanged {0}", route);
            Debug.Log(routeMessage);
        };

        //Token expired callback
        mRtcEngine.OnRequestToken += () =>
        {
            string requestKeyMessage = string.Format("OnRequestToken");
            Debug.Log(requestKeyMessage);
        };

        // Network interrupt callback (triggered only after successful establishment)
        mRtcEngine.OnConnectionInterrupted += () =>
        {
            string interruptedMessage = string.Format("OnConnectionInterrupted");
            Debug.Log(interruptedMessage);
        };

        // Network connection loss callback
        mRtcEngine.OnConnectionLost += () =>
        {
            string lostMessage = string.Format("OnConnectionLost");
            Debug.Log(lostMessage);
        };

        // Set the Log level
        mRtcEngine.SetLogFilter(LOG_FILTER.INFO);

        // 1. Set to free talk mode, usually used for one-on-one or group chatting
        mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_COMMUNICATION);

        //2. Set to live mode, suitable for chat rooms or interactive video streaming scenarios.
        //mRtcEngine.SetChannelProfile (CHANNEL_PROFILE.CHANNEL_PROFILE_LIVE_BROADCASTING);

        //3. Set to game mode. This profile uses a lower bit rate codec and consumes less power. This applies to scenarios where all gamers can talk freely.
        //mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_GAME);

        // Set the user role in the live scene.
        //mRtcEngine.SetClientRole (CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
    }

    private void CheckAppId()
    {
        Debug.Assert(AppID.Length > 10."Please fill in your AppId on the Game Controller object first. .");
        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); }}}}/// <summary>
    ///Join channel
    /// </summary>
    public void JoinChannel()
    {
        // Get the channel name from the input box on the interface
        string channelName = "adc666";

        // 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(); }}/// <summary>
    ///Query the SDK version.
    /// </summary>
    /// <returns></returns>
    public string getSdkVersion()
    {
        string ver = IRtcEngine.GetSdkVersion();
        return ver;
    }


    bool isMuted = false;
    void MuteButtonTapped()
    {
        // Mute or unmute
        string labeltext = isMuted ? "Mute" : "Unmute";
        Text label = muteButton.GetComponentInChildren<Text>();

        if(label ! =null) { label.text = labeltext; } isMuted = ! isMuted;// Mute (stop pushing local audio)mRtcEngine.EnableLocalAudio(! isMuted); Debug.Log("Mute method executed complete"); }}Copy the code

Here I have set the default channel, equal to everyone clicking into a channel to chat

But this is not good! Here is a convenience to do ~

Here we are, we have implemented the world chat channel function in this little game of whack-a-cat!

Although the function may not be perfect, after a few more familiar with the hey hey!


conclusion

  • In this paper, I made a simple ground cat game, which was connected to the audio SDK of Sonnet

  • The small project that oneself work overtime, still need a lot of perfect place

  • This article briefly introduced, or very interesting ha ha! I’ll see you next time.