Unity game example development collection of CutFruit (fruit ninja) leisure games for rapid implementation

Unity game example development collection of CutFruit (fruit ninja) leisure games for rapid implementation

catalogue

Unity game example development collection of CutFruit (fruit ninja) leisure games for rapid implementation

1, Brief introduction

2, Cut fruit (fruit ninja) game content and operation

3, Relevant description

4, Game code framework

5, Knowledge points

6, Game Preview

7, Implementation steps

8, Project source code address

9, Extension

1, Brief introduction

Unity game example development collection, using a simple and understandable way to explain the development and implementation process of common games, so as to facilitate the reference and reuse of similar game development in the later stage.

This section introduces the quick implementation method of CutFruit (fruit ninja) leisure game. I hope it can help you. If it is wrong, please leave a message.

This is a 3D game, which is mainly realized by using sprite map (fruit special effects, etc.), 3D gravity, 3D collision body and trailrender.

2, Cut fruit (fruit ninja) game content and operation

1. At the beginning of the game, the bottom will automatically generate upward moving fruit

2. Press the mouse and move it to generate a blade for cutting fruit

3. When the mouse blade touches the fruit, the fruit will be cut

4. Release the mouse and the cutting blade disappears

5. When you cut fruit, you will get extra points. If you cut a knife bomb, you will lose your life

6. When life is gone, the game is over

3, Relevant description

1. Here, the fruit position is generated at the bottom, and the screen is adapted. The x direction is 20% - 80% of the width, and the y direction is 20% of the downward height of the screen

2. The rule of randomly generating fruits and bombs is: randomly select a certain range, specify a certain number as the bomb, and the others as random fruits

3. Here, Collider 3D collision body is used for 3D fruit and bomb, but Trigger effect is used. Knife can drink fruit collision and Trigger events, but actual collision effect will not occur

4. Here, the OnBecameInvisible() function is used for fruit or fruit fragment recycling (in the editing state, including the window for editing the Scene, not just the Game window). Since the fruit is out of the camera's field of view, the corresponding recycling is carried out, and it can continue to be used in the object pool to save performance

5. Script reproduction: first reproduce the scripts in the Common and Tools folders, then the scripts in the Fruit and Effect folders, then the scripts in the Manager (in the order of implementation steps), and finally GameManager and GameStart

6. It is worth noting here that the function of fruit enumeration: 1) the enumeration value corresponds to the random value of randomly generated fruit; 2) Enumeration is consistent with the corresponding preform name; 3) Enumeration is consistent with the script name of drinking fruit

4, Game code framework

 

5, Knowledge points

1. Monobehavior lifecycle function: wake, Start, Update, Destroy

2. Input key monitors the status of mouse buttons

3,GameObject. Generation of instantiate object, GameObject Destruction of destroy objects

4. Simple object pool management

5. Rigidbody gravity effect, add cireclectider, and conduct collision detection (Trigger)

6. Use of simple UGUI

7. Simple screen adaptation (mainly UI and fruit generation location)

8. Unified constant management of some data, paths, etc

9,Transform.Rotate use

10. Ienumeror orchestration, StartCoroutine starts orchestration and StopAllCoroutines stops using all orchestrations

11. IManager simple interface specification Manager class function

12. Action < int > onchangevalue attribute changes the use of delegates

13,Resources. Load < GameObject > () use of code loading preform

14. A simple tool class for converting screen coordinates to world coordinates

15, SceneManager.LoadScene, and SceneManager Getactivescene() get the current scene

16. Classified management of resource script folders for game development

17. Wait


6, Game Preview

 

7, Implementation steps

1. Open Unity and import fruit and other related resources

 

2. Then make the corresponding resources into preforms

3. This explains the production process of Knife: 1) the picture is a blade, which can be replaced by the picture; 2) Create a trailing material Trail, the corresponding shader is Particles/Additive, and assign a corresponding blade diagram; 3) Create a Sphere, rename it Knife, add a trailrender component, and assign a Trail material. The trailrender # Time should be reduced to 0.1, and the Knife scale should be reduced to 0.2 (which can be adjusted according to the actual fruit size)

 

4. Cancel the MeshRenderer rendering of Knife and keep Collier for triggering collision event with fruit

 

5. Among them, Rigibody, MeshCollider and MeshCollider are added to the fruit preform, and Convex and Is Trigger are checked (only collision events are triggered during collision, and no actual collision effect occurs). The operation of Bob is similar

 

6. BombEffect and Splash are from Spriterenderer image animation

 

7. In the scene, create a GameObject and change its name to World (pos(0,0,0)). Change the default Main Camera (position to (0,0,0)) and Direction Light to World. Similarly, create a GameObject in turn and change its name to SpawnFruitPos, SpawnSplashPos, SpawnKnifePos, SpawnBombPos and SpawnBombEffectPos. The position is (0,0,0). The function is the same as its name, It is used to uniformly manage the corresponding generated game objects

8. Create a GameObject, rename it UI (Pos(0,0,0)), add a Canvas (automatically add EventSystem), set the UI Scale Mode of the Canvas Scaler to Scale with Screen Size, Reference Resolution to 1080 x 1920, and Match to slide to Height

 

9. LifeText is placed on the left, the anchor point is changed, the text is bold and centered, and can be adjusted according to the actual situation

 

10. Place ScoreText on the right, change the anchor point, and the text is bold and displayed in the center. It can be adjusted according to the actual situation

 

11. The GameOverImage picture is spread all over the screen and set to black to increase transparency. The GameOverText text is bold and displayed in the center. The restart gamebutton is enlarged and the text is enlarged. It can be adjusted according to the actual situation, as shown in the figure finally

 

12. Modify the screen to 1080x1920, and the final UI effect is as follows

 

13. Add Enum script in the project to manage all enumerations. At present, it is only fruit type and fruit crushing type enumerations

	/// <summary>
	///Fruit type
	/// </summary>
	public enum FruitType { 
		Apple = 0,
		Lemon,
		Watermelon,

		SUM_COUNT, // Total number of fruit types
	}

	/// <summary>
	///Broken fruit type
	/// </summary>
	public enum FruitBrokenType { 
		Apple_Broken = 0,
		Lemon_Broken,
		Watermelon_Broken,


		SUM_COUNT,  // Total number of broken fruit types
	}

14. Add GameConfig script in the project to manage some data in the game

	/// <summary>
	///Game configuration data
	/// </summary>
	public class GameConfig 
	{
		public const string NAME_SPACE_NAME = "MGP_005CutFruit.";

		// Name of Knife preform (used for collision trigger judgment)
		public const string KNIFE_NAME = "Knife";

		// Uniform depth distance generated by games such as fruit, bomb and Knife
		public const float GAME_OBJECT_Z_VALUE = 5;	

		// The bottom generates objects at intervals of a few seconds
		public const float BOTTOM_SPAWN_INTERVAL_TIME = 2;	

		// Game life (can cut several bombs)
		public const int GAME_LIFE_LENGTH = 3;
		// How many lives are left in the game
		public const int REMAIN_LIFE_IS_GAME_OVER = 0;

		// Minimum and maximum speed of fruit bomb generation upward
		public const float FRUIT_UP_Y_VELOSITY_MIN_VALUE = 8;
		public const float FRUIT_UP_Y_VELOSITY_MAX_VALUE = 10;

		/// <summary>
		///Value range of randomly generated fruit bomb
		///How many random numbers are bombs
		/// </summary>
		public const int FRUIT_RANDOM_MIN_VALUE = 0;
		public const int FRUIT_RANDOM_MAX_VALUE = 15;
		public const int BOMB_RANDOM_VALUE = 10;

		/// <summary>
		///Score of different fruits
		/// </summary>
		public const int FRUIT_APPLE_SCORE = 10;
		public const int FRUIT_LEMON_SCORE = 20;
		public const int FRUIT_WATERMELON_SCORE = 30;

		//Cut to the life lost by the bomb
		public const int BOMB_REDUCE_LIFE = 1;
	}

15. Add gameobjectpathinsecenedefine script in the project to define the path class of game object in the scene, and ResPathDefine defines the path class of preform

	/// <summary>
	///Defines the path class of game objects in the scene
	/// </summary>
	public class GameObjectPathInSceneDefine 
	{
		public const string WORLD_PATH = "World";
		public const string UI_PATH = "UI";

		public const string SPAWN_FRUIT_POS_PATH = "SpawnFruitPos";
		public const string SPAWN_SPLASH_POS_PATH = "SpawnSplashPos";
		public const string SPAWN_KNIFE_POS_PATH = "SpawnKnifePos";
		public const string SPAWN_BMOB_POS_PATH = "SpawnBombPos";
		public const string SPAWN_BMOB_EFFECT_POS_PATH = "SpawnBombEffectPos";


		public const string UI_LIFE_TEXT_PATH = "Canvas/LifeText";
		public const string UI_SCORE_TEXT_PATH = "Canvas/ScoreText";
		public const string UI_GAME_OVER_IMAGE_PATH = "Canvas/GameOverImage";
		public const string UI_RESTART_GAME_BUTTON_PATH = "Canvas/GameOverImage/RestartGameButton";


	}

	/// <summary>
	///Define preform path class
	/// </summary>
	public class ResPathDefine 
	{
		public const string FRUIT_PREFAB_BASE_PATH = "Prefabs/CutFruits/";

		public const string SPLASH_PREFAB_PATH = "Prefabs/Splash";
		public const string KNIFE_PREFAB_PATH = "Prefabs/Knife";
		public const string BOMB_PREFAB_PATH = "Prefabs/Bomb";
		public const string BOMB_EFFECT_PREFAB_PATH = "Prefabs/BombEffect";
	}

 

16. BaseFruit script and fruit base script are added to the project, which mainly manages the collision events between fruit and Knife (bonus points, generation of fruit fragments, special effects, etc.), as well as the recycling of the camera out of the field. The key codes are as follows

        /// <summary>
        ///Collision event
        /// </summary>
        /// <param name="other"></param>
        private void OnTriggerEnter(Collider other)
        {
            if (other.name.StartsWith( GameConfig.KNIFE_NAME))
            {
                //Increase score
                m_DataModelManager.Score.Value += Score;


                for (int i = 0; i < 2; i++)
                {
                    //Produce broken fruit
                    FruitBroken fruitBroken = SpawnBroken();
                    fruitBroken.Init(FruitBrokenType,m_FruitManager);
                    GameObject temp = fruitBroken.gameObject;
                    temp.SetActive(true);
                    temp.transform.position = transform.position;
                    temp.transform.eulerAngles = new Vector3(Random.Range(0, 360), Random.Range(0, 360), Random.Range(0, 360));
                    //-3 -2  2 3

                    // Random fruit fragment velocity
                    float x, y;
                    int ranX = Random.Range(0, 2);
                    if (ranX == 0)
                    {
                        x = Random.Range(-3.0f, -2.0f);
                    }
                    else
                    {
                        x = Random.Range(2.0f, 3.0f);
                    }

                    int ranY = Random.Range(0, 2);
                    if (ranY == 0)
                    {
                        y = Random.Range(-3.0f, -2.0f);
                    }
                    else
                    {
                        y = Random.Range(2.0f, 3.0f);
                    }

                    temp.GetComponent<Rigidbody>().velocity = new Vector2(x, y);

                }

                //Produce juice effect
                Splash splash = SpawnSplash();
                splash.transform.position = transform.position;
                splash.transform.rotation = Quaternion.Euler(Vector3.forward * Random.Range(-180,180));
                splash.Show(FruitSplahColor, m_SplashManager.RecycleSplah);
                

                //Hide fruit objects
                m_FruitManager.RecycleFruit(FruitType,this);
                m_IsRecycle = false;
            }
            if (other.tag == "BottomCollider")
            {
                gameObject.SetActive(false);
            }
        }

        private void OnBecameVisible()
        {
            m_IsRecycle = true;
        }

        /// <summary>
        ///Camera field of view
        ///Recycled fruit
        /// </summary>
        private void OnBecameInvisible()
        {
            if (m_IsRecycle==true)
            {
                m_FruitManager.RecycleFruit(FruitType, this);
                m_IsRecycle = false;

            }
        }

17. Add Apple, Lemon and Watermelon scripts in the project, inherit BaseFruit, implement abstract classes, and set their corresponding fruit types, fruit crushing types, fruit smash colors, and scores

	public class Apple : BaseFruit
	{
       

        public override FruitType FruitType => FruitType.Apple;

        public override FruitBrokenType FruitBrokenType => FruitBrokenType.Apple_Broken;

        public override Color FruitSplahColor => Color.red;

        public override int Score => GameConfig.FRUIT_APPLE_SCORE;
    }

18. The FruitManager script is added in the project to mainly manage the preform loading of fruits and fruit fragments and the initialization of fruit and fruit fragment object pool, and provide interfaces to obtain fruits and fruit fragments from the object pool and recycle fruits and fruit fragments

        /// <summary>
        ///Get fruit fragments
        /// </summary>
        /// <param name="fruitBrokenType"></param>
        /// <returns></returns>
        public FruitBroken GetFruitBroken(FruitBrokenType fruitBrokenType)
        {
            Queue<FruitBroken> fruitBrokenQue = m_FruitBrokenObjectPoolDict[fruitBrokenType];
            if (fruitBrokenQue.Count > 0)
            {
                return fruitBrokenQue.Dequeue();
            }
            else
            {
                return InstantiateFruitBroken(fruitBrokenType);
            }
        }

        /// <summary>
        ///Recycled fruit
        /// </summary>
        /// <param name="fruitType"></param>
        /// <param name="baseFruit"></param>
        public void RecycleFruit(FruitType fruitType,BaseFruit baseFruit) {
            baseFruit.gameObject.SetActive(false);
            if (m_FruitObjectPoolDict!=null && m_FruitObjectPoolDict[fruitType]!=null)
            {
                m_FruitObjectPoolDict[fruitType].Enqueue(baseFruit);

            }
        }

        /// <summary>
        ///Recycling fruit fragments
        /// </summary>
        /// <param name="fruitBrokenType"></param>
        /// <param name="fruitBroken"></param>
        public void RecycleFruitBroken(FruitBrokenType fruitBrokenType, FruitBroken fruitBroken)
        {
            fruitBroken.gameObject.SetActive(false);
            m_FruitBrokenObjectPoolDict[fruitBrokenType].Enqueue(fruitBroken);
        }

        /// <summary>
        ///Load preforms and preload fruits into the object pool
        /// </summary>
        private void InitFruit() {

            // Loading preform
            m_FruitPrefabsDict = new Dictionary<string, GameObject>();
            for (FruitType fruitType = FruitType.Apple; fruitType < FruitType.SUM_COUNT; fruitType++)
            {
                GameObject prefab = Resources.Load<GameObject>(ResPathDefine.FRUIT_PREFAB_BASE_PATH+fruitType.ToString());
                if (prefab == null)
                {
                    Debug.LogError(GetType() + "/InitFruit()/prefab is null, path = " + ResPathDefine.FRUIT_PREFAB_BASE_PATH + fruitType.ToString());
                }
                else {
                    m_FruitPrefabsDict.Add(fruitType.ToString(),prefab);
                }
            }

            // Loading preform
            for (FruitBrokenType fruitBrokenType = FruitBrokenType.Apple_Broken; fruitBrokenType < FruitBrokenType.SUM_COUNT; fruitBrokenType++)
            {
                GameObject prefab = Resources.Load<GameObject>(ResPathDefine.FRUIT_PREFAB_BASE_PATH + fruitBrokenType.ToString());
                if (prefab == null)
                {
                    Debug.LogError(GetType() + "/InitFruit()/prefab is null, path = " + ResPathDefine.FRUIT_PREFAB_BASE_PATH + fruitBrokenType.ToString());
                }
                else
                {
                    m_FruitPrefabsDict.Add(fruitBrokenType.ToString(), prefab);
                }
            }

            // Preloaded fruit
            m_FruitObjectPoolDict = new Dictionary<FruitType, Queue<BaseFruit>>();
            for (FruitType fruitType = FruitType.Apple; fruitType < FruitType.SUM_COUNT; fruitType++) {
                Queue<BaseFruit> fruits = new Queue<BaseFruit>();
                m_FruitObjectPoolDict.Add(fruitType, fruits);

                RecycleFruit(fruitType, InstantiateFruit(fruitType));
            }

            // Preload Broken
            m_FruitBrokenObjectPoolDict = new Dictionary<FruitBrokenType, Queue<FruitBroken>>();
            for (FruitBrokenType fruitBrokenType = FruitBrokenType.Apple_Broken; fruitBrokenType < FruitBrokenType.SUM_COUNT; fruitBrokenType++)
            {
                Queue<FruitBroken> fruitBrokens = new Queue<FruitBroken>();
                m_FruitBrokenObjectPoolDict.Add(fruitBrokenType, fruitBrokens);

                RecycleFruitBroken(fruitBrokenType, InstantiateFruitBroken(fruitBrokenType));
            }
        }

        /// <summary>
        ///Generates the specified fruit object
        /// </summary>
        /// <param name="fruitType"></param>
        /// <returns></returns>
        private BaseFruit InstantiateFruit(FruitType fruitType) {
            GameObject fruit = GameObject.Instantiate(m_FruitPrefabsDict[fruitType.ToString()], m_SpawnFruitPosTrans);
            // Get script type through reflection and add script (note adding namespace)
            Type type = Type.GetType( GameConfig.NAME_SPACE_NAME+ fruitType.ToString());
            return (fruit.AddComponent(type) as BaseFruit);
        }

        /// <summary>
        ///Produce broken fruit
        /// </summary>
        /// <param name="fruitBrokenType"></param>
        /// <returns></returns>
        private FruitBroken InstantiateFruitBroken(FruitBrokenType fruitBrokenType)
        {
            GameObject fruit = GameObject.Instantiate(m_FruitPrefabsDict[fruitBrokenType.ToString()], m_SpawnFruitPosTrans);
          
            return fruit.AddComponent<FruitBroken>();
        }

19. Add a Bomb script in the project. The main functions are the collision between Bob and Knife (reduce health and generate special effects), and the recovery of the camera after viewing the field

        private void OnTriggerEnter(Collider other)
        {
            if (other.name.StartsWith( GameConfig.KNIFE_NAME))
            {
                //Reduce health
                m_DataModelManager.Life.Value -= GameConfig.BOMB_REDUCE_LIFE;

                //Generate explosion effects
                BombEffect bombEffect = m_BombEffectManager.GetBombEffect();
                bombEffect.Show(transform.position, m_BombEffectManager.RecycleBombEffect);

                //Hidden object
                m_BmobManager.RecycleBomb(this);
            }
        }


        private void OnBecameVisible()
        {
            m_IsRecycle = true;
        }


        /// <summary>
        ///The camera is a field event
        /// </summary>
        private void OnBecameInvisible()
        {
            if (m_IsRecycle == true)
            {
                m_BmobManager.RecycleBomb( this);
                m_IsRecycle = false;

            }
        }

20. Add the BombManager script in the project, which mainly manages the loading of Bob preforms, initialization of object pool, and provides interfaces to obtain and recycle Bob

        /// <summary>
        ///Get Bob 
        /// </summary>
        /// <returns></returns>
        public Bomb GetBmob()
        {
            if (m_BombQueue.Count > 0)
            {
                Bomb bomb = m_BombQueue.Dequeue();
                bomb.gameObject.SetActive(true);
                return bomb;
            }
            else
            {
                return InstantiateBomb();
            }
        }

        /// <summary>
        ///Recycle Bob 
        /// </summary>
        /// <param name="bomb"></param>
        public void RecycleBomb(Bomb bomb)
        {
            bomb.gameObject.SetActive(false);
            m_BombQueue.Enqueue(bomb);
        }

        /// <summary>
        ///Initialization
        /// </summary>
        private void InitBmob()
        {
            // Loading preform
            m_BombPrefab = Resources.Load<GameObject>(ResPathDefine.BOMB_PREFAB_PATH);

            // Preload Bob
            m_BombQueue = new Queue<Bomb>();
            RecycleBomb(InstantiateBomb());
        }

        private Bomb InstantiateBomb()
        {
            GameObject go = GameObject.Instantiate(m_BombPrefab, m_SpawnBombPosTrans);

            return go.AddComponent<Bomb>();
        }

21. Add the BombEffect script in the project. The main function is to display special effects and recover special effects regularly

		/// <summary>
		///Display effects
		/// </summary>
		/// <param name="pos"></param>
		/// <param name="showAnimationEndAction"></param>
		public void Show(Vector3 pos, Action<BombEffect> showAnimationEndAction)
		{
			// Assign position and random rotation, and recycle regularly
			transform.position = pos;
			transform.rotation = Quaternion.Euler(Vector3.forward * UnityEngine.Random.Range(-180, 180));
			StartCoroutine(Recycle(showAnimationEndAction));

		}

		/// <summary>
		///Synergetic recovery effect
		/// </summary>
		/// <param name="showAnimationEndAction"></param>
		/// <returns></returns>
		IEnumerator Recycle(Action<BombEffect> showAnimationEndAction) {
			yield return new WaitForSeconds(m_AnimationLength);
            if (showAnimationEndAction!=null)
            {
				showAnimationEndAction.Invoke(this);

			}
		}

22. Add the BombEffectManager script in the project, which mainly manages the loading of bombpreforms, initialization of object pool, and provides interfaces to obtain and recycle bombeffects

        /// <summary>
        ///Get explosion effects
        /// </summary>
        /// <returns></returns>
        public BombEffect GetBombEffect()
        {
            if (m_BombEffectQueue.Count > 0)
            {
                BombEffect bombEffect = m_BombEffectQueue.Dequeue();
                bombEffect.gameObject.SetActive(true);
                return bombEffect;
            }
            else
            {
                return InstantiateBombEffect();
            }
        }

        /// <summary>
        ///Recycling explosion effects
        /// </summary>
        /// <param name="bombEffect"></param>
        public void RecycleBombEffect(BombEffect bombEffect)
        {
            bombEffect.gameObject.SetActive(false);
            m_BombEffectQueue.Enqueue(bombEffect);
        }

        /// <summary>
        ///Initialize effects
        /// </summary>
        private void InitBombEffect()
        {
            // Loading preform
            m_BombEffectPrefab = Resources.Load<GameObject>(ResPathDefine.BOMB_EFFECT_PREFAB_PATH);

            // Preload BombEffect
            m_BombEffectQueue = new Queue<BombEffect>();
            RecycleBombEffect(InstantiateBombEffect());
        }

        private BombEffect InstantiateBombEffect()
        {
            GameObject go = GameObject.Instantiate(m_BombEffectPrefab, m_SpawnBombEffectPosTrans);

            return go.AddComponent<BombEffect>();
        }

23. Add Splash script in the project. The main function is to display Splash and recycle Splash regularly

		/// <summary>
		///Display effects
		/// </summary>
		/// <param name="color"></param>
		/// <param name="showAnimationEndAction"></param>
		public void Show(Color32 color, Action<Splash> showAnimationEndAction)
		{
			StartCoroutine(EffectAnimation(color, showAnimationEndAction));
		}

		/// <summary>
		///Special effects animation
		/// </summary>
		/// <param name="color"></param>
		/// <param name="showAnimationEndAction"></param>
		/// <returns></returns>
		IEnumerator EffectAnimation(Color color, Action<Splash> showAnimationEndAction)
		{
			m_ColorValue = 0;
			color.a = 0;
			SpriteRenderer.color = color;
			while (true)
			{
				// lerp uniform interpolation
				m_ColorValue += 1.0f / SPEED * Time.deltaTime;
				color.a = Mathf.Lerp(color.a, 1, m_ColorValue);
				SpriteRenderer.color = color;
				if ((1 - color.a <= 0.05f))
				{
					color.a = 1;
					SpriteRenderer.color = color;

					break;
				}


				yield return new WaitForEndOfFrame();
			}
			m_ColorValue = 0;
			while (true)
			{
				// lerp uniform interpolation
				m_ColorValue += 1.0f / SPEED * Time.deltaTime;
				color.a = Mathf.Lerp(color.a, 0, m_ColorValue);
				SpriteRenderer.color = color;
				if ((color.a - 0) <= 0.05f)
				{
					color.a = 0;
					SpriteRenderer.color = color;
					break;
				}


				yield return new WaitForEndOfFrame();
			}


			if (showAnimationEndAction != null)
			{
				showAnimationEndAction.Invoke(this);
			}
		}

24. Add Splash manager script in the project, which mainly manages the loading of Bob preform, initialization of object pool, and provides interface to obtain and recycle Splash

        /// <summary>
        ///Get Splash
        /// </summary>
        /// <returns></returns>
        public Splash GetSplash()
        {
            if (m_SplashQueue.Count > 0)
            {
                Splash splash = m_SplashQueue.Dequeue();
                splash.gameObject.SetActive(true);
                return splash;
            }
            else
            {
                return InstantiateSplash();
            }
        }

        /// <summary>
        ///Recycle Splash
        /// </summary>
        /// <param name="splash"></param>
        public void RecycleSplah(Splash splash)
        {
            splash.gameObject.SetActive(false);
            m_SplashQueue.Enqueue(splash);
        }

        /// <summary>
        ///Initialize SPlash
        /// </summary>
        private void InitSplash()
        {
            // Loading preform
            m_SplashPrefab = Resources.Load<GameObject>(ResPathDefine.SPLASH_PREFAB_PATH);

            // Preload Splash
            m_SplashQueue = new Queue<Splash>();
            RecycleSplah(InstantiateSplash());
        }

        private Splash InstantiateSplash()
        {
            GameObject go = GameObject.Instantiate(m_SplashPrefab, m_SpawnSplashPosTrans);

            return go.AddComponent<Splash>();
        }

25. Add Tools script in the project. The main function is to convert screen coordinates to world coordinates

public class Tools
	{
		/// <summary>
		///Convert screen coordinates to world coordinates
		/// </summary>
		///< param name = "reftran" > corresponding reference object < / param >
		///< param name = "refcamera" > corresponding reference camera < / param >
		///< param name = "screenpos" > screen position < / param >
		///< returns > world position of screen position < / returns >
		public static Vector3 ScreenPosToWorldPos(Transform refTran, Camera refCamera, Vector2 screenPos)
		{
			//Replace object coordinates with screen coordinates
			Vector3 pos = refCamera.WorldToScreenPoint(refTran.position);
			//Make the screen coordinates of the mouse consistent with the object coordinates
			Vector3 mousePos = new Vector3(screenPos.x, screenPos.y, pos.z);
			//Replace the correct mouse screen coordinates with world coordinates and give them to the object
			return refCamera.ScreenToWorldPoint(mousePos);

		}
	}

26. The KnifeManager script is added in the project to mainly manage the loading of Knife preforms, monitor the mouse press, and perform the function of cutting fruit with the blade

        /// <summary>
        ///Loading preform
        /// </summary>
        void LoadPrefab() {
            m_KnifePrefab = Resources.Load<GameObject>(ResPathDefine.KNIFE_PREFAB_PATH);
            if (m_KnifePrefab == null)
            {
                Debug.LogError(GetType() + "/LoadPrefab()/ prefab  is  null, path = " + ResPathDefine.KNIFE_PREFAB_PATH);
            }
            else {
                m_KnifeTrans = GameObject.Instantiate(m_KnifePrefab,m_SpawnKnifePosTrans).transform;
                m_KnifeTrans.position = Vector3.forward * GameConfig.GAME_OBJECT_Z_VALUE;
                m_KnifeTrans.gameObject.SetActive(false);
            }
        }

        /// <summary>
        ///When the game is not over, press the mouse to update the blade
        /// </summary>
        void UpdateShowKnife() {
            if (m_KnifeTrans!=null && m_IsGameOver==false)
            {
                if (Input.GetMouseButtonDown(0))
                {
                    m_KnifeTrans.position = Tools.ScreenPosToWorldPos(m_KnifeTrans, Camera.main, Input.mousePosition);
                    m_KnifeTrans.gameObject.SetActive(true);
                }
                else if (Input.GetMouseButton(0))
                {
                    m_KnifeTrans.position = Tools.ScreenPosToWorldPos(m_KnifeTrans, Camera.main, Input.mousePosition);
                }
                else if (Input.GetMouseButtonUp(0))
                {
                    m_KnifeTrans.gameObject.SetActive(false);
                }
            }
            
        }

27. Add a Model script to the project. Its main functions are score and life data models, including numerical records and numerical change event delegation

	/// <summary>
	///Data model
	/// </summary>
	public class Model 
	{
		private int m_Value;
		public int Value
		{
			get { return m_Value; }
			set
			{
				if (m_Value != value)
				{
					m_Value = value;

					if (OnValueChanged != null)
					{
						OnValueChanged.Invoke(value);

					}
				}
			}
		}

		/// <summary>
		///Numerical change event
		/// </summary>
		public Action<int> OnValueChanged;
	}

28. Add a DataModelManager script to the project, which mainly manages Score and Life

        public Model Score => m_Scroe;
        public Model Life => m_Life;

        public void Init(Transform rootTrans, params object[] manager)
        {
            m_Scroe = new Model();
            m_Scroe.Value = 0;
            m_Life = new Model();
            m_Life.Value = GameConfig.GAME_LIFE_LENGTH;
        }

29. Add UIManager script in the project, which mainly manages UI related acquisition, data display, game end and button event to reload the game

        private Text m_LifeText;
        private Text m_ScoreText;
        private GameObject m_GameOverImageGo;
        private Button m_RestartGameButton;

        private DataModelManager m_DataModelManager;
        public void Init(Transform rootTrans, params object[] managers)
        {
            m_LifeText = rootTrans.Find(GameObjectPathInSceneDefine.UI_LIFE_TEXT_PATH).GetComponent<Text>();
            m_ScoreText = rootTrans.Find(GameObjectPathInSceneDefine.UI_SCORE_TEXT_PATH).GetComponent<Text>();
            m_GameOverImageGo = rootTrans.Find(GameObjectPathInSceneDefine.UI_GAME_OVER_IMAGE_PATH).gameObject;
            m_RestartGameButton = rootTrans.Find(GameObjectPathInSceneDefine.UI_RESTART_GAME_BUTTON_PATH).GetComponent<Button>();

            m_DataModelManager = managers[0] as DataModelManager;

            m_GameOverImageGo.SetActive(false);
            m_LifeText.text = m_DataModelManager.Life.Value.ToString();
            m_ScoreText.text = m_DataModelManager.Score.Value.ToString();
            m_DataModelManager.Life.OnValueChanged += OnLifeValueChanged;
            m_DataModelManager.Score.OnValueChanged += OnScroeValueChanged;
            m_RestartGameButton.onClick.AddListener(OnRestartButton);
        }

        public void OnGameOver()
        {
            m_GameOverImageGo.SetActive(true);
        }

        private void OnLifeValueChanged(int life)
        {
            m_LifeText.text = life.ToString();
        }

        private void OnScroeValueChanged(int score)
        {
            m_ScoreText.text = score.ToString();
        }

        private void OnRestartButton()
        {
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        }

30. Add GameManager script (single instance script) to the project. The main functions are: 1) obtain relevant game objects or UI in the scene, 2) new related Manager management classes, and initialize Init, Update, and Destroy; 3) Regularly generate fruit and bombs; 4) Judge whether the game is over;

        /// <summary>
        ///Bottom generation
        /// </summary>
        private void BottomSpawnFruitAndBomb()
        {
            GameObject go = null;
            // Random number, judge whether to generate fruit or bomb
            int ran = Random.Range(GameConfig.FRUIT_RANDOM_MIN_VALUE, GameConfig.FRUIT_RANDOM_MAX_VALUE);
            if (ran != GameConfig.BOMB_RANDOM_VALUE)
            {
                BaseFruit fruit = m_FruitManager.GetRandomFruit();
                fruit.Init(m_FruitManager, m_SplashManager, m_DataModelManager);
                go = fruit.gameObject;
            }
            else
            {
                Bomb bomb = m_BmobManager.GetBmob();
                bomb.Init(m_BmobManager,m_BombEffectManager,m_DataModelManager);
                go = bomb.gameObject;
            }

            
            // Generate the position and velocity of the object
            go.transform.position = new Vector3(Random.Range(m_Bottom_Spawn_X_Min, m_Bottom_Spawn_X_Max), m_Bottom_Spawn_Y, GameConfig.GAME_OBJECT_Z_VALUE);
            go.transform.rotation = Quaternion.identity;
            go.GetComponent<Rigidbody>().velocity = new Vector2(0, Random.Range(GameConfig.FRUIT_UP_Y_VELOSITY_MIN_VALUE,
                GameConfig.FRUIT_UP_Y_VELOSITY_MAX_VALUE));

        }

        /// <summary>
        ///Determine whether the game is over
        /// </summary>
        /// <param name="life"></param>
        void JudageGameOverByLife(int life) {
            if (life == GameConfig.REMAIN_LIFE_IS_GAME_OVER)
            {
                OnGameOver();
            }
        }

        /// <summary>
        ///End operation
        /// </summary>
        void OnGameOver() {
            m_IsGameOver = true;
            m_UIManager.OnGameOver();
            m_KnifeManager.OnGameOver();
        }

31. Add the GameStart script in the project, the entrance of the whole game, and manage the corresponding functions of wake(), Start(), Update(), OnDestroy() of the corresponding GameManager

    /// <summary>
    ///Game entrance
    /// </summary>
	public class GameStart : MonoBehaviour
	{
        private void Awake()
        {
            GameManager.Instance.Awake(this);
        }

        // Start is called before the first frame update
        void Start()
		{
            GameManager.Instance.Start();
        }

		// Update is called once per frame
		void Update()
		{
            GameManager.Instance.Update();
        }

        private void OnDestroy()
        {
            GameManager.Instance.Destroy();
        }
     }

32. Add a GameObject empty object in the scene, rename it GameStart, and mount the GameStart script

34. Run the game to produce fruit automatically. Press the mouse to cut the fruit

8, Project source code address

github address: GitHub - xankui / unity minigameparadise: unity game development set code set

MGP of_ 005cutfruit project

9, Extension

The game is fun, interesting, visual and many other factors. The following briefly introduces several aspects to expand the direction of the game for reference only

1. You can modify game resources and skin changes according to your needs

2. You can add bonus effects, sound effects, more details of the background, etc. as needed

3. Add UI panels, etc. to beautify the game

4. Effect can set different types of effects or 3D particle effects

5. Special fruit, special events, freezing, fire, etc;

6. Add the highest score reservation, game leaderboard, etc;

7. The special effects of bombs can be set with different bombs and different special effects;

8. Only bottom generation is set for fruit generation. You can add two side generation or fruit bomb generation

9. It can be set, the number of fruits with different wave numbers, intermittent frequency and timed game time

10. Wait
 

Keywords: Unity Game Development

Added by greywire on Sat, 22 Jan 2022 03:49:58 +0200