首页 > 代码库 > Road Crossing Game Template 学习

Road Crossing Game Template 学习

技术分享

技术分享

技术分享
using UnityEngine;
using System;

namespace RoadCrossing.Types
{
    /// <summary>
    /// 小路
    /// </summary>
    [Serializable]
    public class Lane
    {
        /// <summary>
        /// 小路相关的游戏物体
        /// </summary>
        public Transform laneObject;
        /// <summary>
        /// 生成这条路的几率
        /// </summary>
        public int laneChance = 1;
        /// <summary>
        /// 小路的宽度
        /// </summary>
        public float laneWidth = 1;
        /// <summary>
        /// 生成物品的几率
        /// </summary>
        public float itemChance = 1;
    }
}
Lane
技术分享
using UnityEngine;
using System;
namespace RoadCrossing.Types
{
    /// <summary>
    /// 掉落物品
    /// </summary>
    [Serializable]
    public class ObjectDrop
    {
        /// <summary>
        /// 掉落物品
        /// </summary>
        public Transform droppedObject;
        
        /// <summary>
        /// 掉落几率
        /// </summary>
        public int dropChance = 1;
    }
}
ObjectDrop
技术分享
using UnityEngine;
using System;

namespace RoadCrossing.Types
{
    /// <summary>
    /// 商店道具
    /// </summary>
    [Serializable]
    public class ShopItem
    {
        /// <summary>
        /// 这个道具关联的按钮
        /// </summary>
        public Transform itemButton;
        
        /// <summary>
        /// 解锁状态,0 未解锁 1 已解锁
        /// </summary>
        public int lockState = 0;
        
        /// <summary>
        /// 解锁这个道具的花费
        /// </summary>
        public int costToUnlock = 100;
        
        /// <summary>
        /// 这个道具在存档中的键名
        /// </summary>
        public string playerPrefsName = "FroggyUnlock";
    }
}
ShopItem
技术分享
using System;

namespace RoadCrossing.Types
{
    /// <summary>
    /// 接触后调用的函数信息
    /// </summary>
    [Serializable]
    public class TouchFunction
    {
        /// <summary>
        /// 调用的函数名称
        /// </summary>
        public string functionName = "CancelMove";
        
        /// <summary>
        /// 在哪个目标上调用函数
        /// </summary>
        public string targetTag = "Player";
        
        /// <summary>
        /// 传递给函数的参数
        /// </summary>
        public float functionParameter = 0;
    }
}
TouchFunction
技术分享
using UnityEngine;
using RoadCrossing.Types;

namespace RoadCrossing
{
    /// <summary>
    /// 能与玩家交互的块,可以是岩石,墙,敌人,金币
    /// </summary>
    public class RCGBlock : MonoBehaviour
    {
        /// <summary>
        /// 能够接触这个块的物体的标签
        /// </summary>
        public string touchTargetTag = "Player";
    
        /// <summary>
        /// 这个块被目标接触后调用的函数列表
        /// </summary>
        public TouchFunction[] touchFunctions;
    
        /// <summary>
        /// 经过多少次接触后删除这个物体
        /// </summary>
        public int removeAfterTouches = 0;
        /// <summary>
        /// 这个物体是否可以被移除
        /// </summary>
        internal bool  isRemovable = false;
    
        /// <summary>
        /// 当这个物体被接触后的动画剪辑
        /// </summary>
        public AnimationClip hitAnimation;
    
        /// <summary>
        /// 当这个物体被接触后的声音剪辑
        /// </summary>
        public AudioClip soundHit;

        /// <summary>
        /// AudioSource所属物体的标签
        /// </summary>
        public string soundSourceTag = "GameController";
    
        /// <summary>
        /// 物体死亡后创建的死亡的特效
        /// </summary>
        public Transform deathEffect;
    

        void Start()
        {
            if( removeAfterTouches > 0 )
                isRemovable = true;
        }
    
        void OnTriggerEnter(Collider other)
        {    
            if( other.tag == touchTargetTag )
            {
                //调用目标物体上的函数
                foreach( var touchFunction in touchFunctions )
                {
                    if( touchFunction.targetTag != string.Empty && touchFunction.functionName != string.Empty )
                    {
                        GameObject.FindGameObjectWithTag(touchFunction.targetTag).SendMessage(touchFunction.functionName, touchFunction.functionParameter);
                    }
                }
            
                //满足条件,播放动画
                if( GetComponent<Animation>() && hitAnimation )
                {
                    GetComponent<Animation>().Stop();
                    GetComponent<Animation>().Play(hitAnimation.name);
                }
            
                //满足条件,删除物体,创建死亡特效
                if( isRemovable == true )
                {
                    removeAfterTouches--;
                
                    if( removeAfterTouches <= 0 )
                    {
                        if( deathEffect )
                            Instantiate(deathEffect, transform.position, Quaternion.identity);
                    
                        Destroy(gameObject);
                    }
                }
            
                //满足条件,播放声音
                if( soundSourceTag != string.Empty && soundHit )
                    GameObject.FindGameObjectWithTag(soundSourceTag).GetComponent<AudioSource>().PlayOneShot(soundHit);
            }
        }
    }
}
RCGBlock
技术分享
using UnityEngine;

namespace RoadCrossing
{
    /// <summary>
    /// 控制上下左右点击的按钮(collider)
    /// </summary>
    public class RCGButtonFunction : MonoBehaviour
    {
        /// <summary>
        /// 要执行函数的物体
        /// </summary>
        public Transform functionTarget;
    
        /// <summary>
        /// 要执行的函数名称
        /// </summary>
        public string functionName;
    
        /// <summary>
        /// 传递给函数的参数
        /// </summary>
        public string functionParameter;
    
        /// <summary>
        /// 点击声音
        /// </summary>
        public AudioClip soundClick;
        /// <summary>
        /// AudioSource 所属的物体
        /// </summary>
        public Transform soundSource;

        void onm ouseDrag()
        {
            ExecuteFunction();
        }
    
        /// <summary>
        /// 执行函数
        /// </summary>
        void ExecuteFunction()
        {
            if( soundSource )
                if( soundSource.GetComponent<AudioSource>() )
                    soundSource.GetComponent<AudioSource>().PlayOneShot(soundClick);
        
            if( functionName != string.Empty )
            {  
                if( functionTarget )
                {
                    functionTarget.SendMessage(functionName, functionParameter);
                }
            }
        }
    }
}
RCGButtonFunction
技术分享
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using RoadCrossing.Types;

namespace RoadCrossing
{
        
    /// <summary>
    /// 游戏控制
    /// </summary>
    public class RCGGameController : MonoBehaviour
    {
        /// <summary>
        /// 玩家物体列表
        /// </summary>
        public Transform[] playerObjects;
        /// <summary>
        /// 当前玩家物体索引
        /// </summary>
        public int currentPlayer;
    
        /// <summary>
        /// 跟随玩家的相机
        /// </summary>
        public Transform cameraObject;

        /// <summary>
        /// 控制玩家移动的按钮(collider),如果是滑动控制,这些物体会被禁用
        /// </summary>
        public Transform moveButtonsObject;
        
        /// <summary>
        /// 是否使用滑动控制
        /// </summary>
        public bool swipeControls = false;
        
        /// <summary>
        /// 滑动起点
        /// </summary>
        internal Vector3 swipeStart;
        /// <summary>
        /// 滑动终点
        /// </summary>
        internal Vector3 swipeEnd;
        
        public float swipeDistance = 10;

        /// <summary>
        /// 滑动命令取消前的等待时间
        /// </summary>
        public float swipeTimeout = 1;
        /// <summary>
        /// 
        /// </summary>
        internal float swipeTimeoutCount;
    
        /// <summary>
        /// 所有的小路预设列表
        /// </summary>
        public Lane[] lanes;
        internal Lane[] lanesList;    
    
        public ObjectDrop[] objectDrops;
        internal Transform[] objectDropList;

        public int objectDropOffset = 4;
    
        /// <summary>
        /// 游戏开始前与预创建的小路数量
        /// </summary>
        public int precreateLanes = 20;
        /// <summary>
        /// 下一条小路的位置
        /// </summary>
        internal float nextLanePosition = 1;
    
        /// <summary>
        /// 得分
        /// </summary>
        public int score = 0;
        /// <summary>
        /// 显示得分的文本
        /// </summary>
        public Transform scoreText;
        /// <summary>
        /// 最高分数
        /// </summary>
        internal int highScore = 0;

        /// <summary>
        /// 游戏中总共获得的分数
        /// </summary>
        public string coinsPlayerPrefs = "Coins";
    
        /// <summary>
        /// 游戏速度
        /// </summary>
        public float gameSpeed = 1;
    
        /// <summary>
        /// 玩家升级需要的点数
        /// </summary>
        public int levelUpEveryCoins = 5;
        /// <summary>
        /// 增加的分数
        /// </summary>
        internal int increaseCount = 0;
    
        /// <summary>
        /// 在玩家身后追逐玩家的物体
        /// </summary>
        public Transform deathLineObject;
        /// <summary>
        /// 追杀物体的移动速度
        /// </summary>
        public float deathLineSpeed = 1;
        /// <summary>
        /// 追杀物体每次增加的速度的值
        /// </summary>
        public float deathLineSpeedIncrease = 1;
        /// <summary>
        /// 追杀物体的最大移动速度
        /// </summary>
        public float deathLineSpeedMax = 1.5f;

        /// <summary>
        /// 游戏面板
        /// </summary>
        public Transform gameCanvas;
        /// <summary>
        /// 暂停面板
        /// </summary>
        public Transform pauseCanvas;
        /// <summary>
        /// 游戏结束面板
        /// </summary>
        public Transform gameOverCanvas;
    
        /// <summary>
        /// 游戏是否结束
        /// </summary>
        internal bool  isGameOver = false;
    
        /// <summary>
        /// 主目录场景
        /// </summary>
        public string mainMenuLevelName = "MainMenu";
    
        /// <summary>
        /// 升级声音
        /// </summary>
        public AudioClip soundLevelUp;
        /// <summary>
        /// 游戏结束声音
        /// </summary>
        public AudioClip soundGameOver;
    
        /// <summary>
        /// 使用AudioSource的物体的标签
        /// </summary>
        public string soundSourceTag = "GameController";
        /// <summary>
        /// 确认按钮
        /// </summary>
        public string confirmButton = "Submit";
    
        //暂停按钮
        public string pauseButton = "Cancel";
        /// <summary>
        /// 游戏是否暂停
        /// </summary>
        internal bool  isPaused = false;
        /// <summary>
        /// 
        /// </summary>
        internal int index = 0;
    
        void Start()
        {
            ChangeScore(0);
        
            //隐藏游戏结束面板
            if( gameOverCanvas )
                gameOverCanvas.gameObject.SetActive(false);
        
            //获取存档中的最高分数
            highScore = PlayerPrefs.GetInt(Application.loadedLevelName + "_HighScore", 0);
        
            int totalLanes = 0;
            int totalLanesIndex = 0;
        
            for(index = 0; index < lanes.Length; index++)
            {
                totalLanes += lanes[index].laneChance;
            }
        
            lanesList = new Lane[totalLanes];
        
            for(index = 0; index < lanes.Length; index++)
            {
                int laneChanceCount = 0;
            
                while( laneChanceCount < lanes[index].laneChance )
                {
                    lanesList[totalLanesIndex] = lanes[index];
                
                    laneChanceCount++;
                
                    totalLanesIndex++;
                }
            }
        
            int totalDrops = 0;
            int totalDropsIndex = 0;
        
            for(index = 0; index < objectDrops.Length; index++)
            {
                totalDrops += objectDrops[index].dropChance;
            }
        
            objectDropList = new Transform[totalDrops];
        
            for(index = 0; index < objectDrops.Length; index++)
            {
                int dropChanceCount = 0;
            
                while( dropChanceCount < objectDrops[index].dropChance )
                {
                    objectDropList[totalDropsIndex] = objectDrops[index].droppedObject;
                
                    dropChanceCount++;
                
                    totalDropsIndex++;
                }
            }

            //从存档中获取当前玩家
            currentPlayer = PlayerPrefs.GetInt("CurrentPlayer", currentPlayer);

            SetPlayer(currentPlayer);
        
            if( cameraObject == null )
                cameraObject = GameObject.FindGameObjectWithTag("MainCamera").transform;
        
            CreateLane(precreateLanes);
        
            
            Pause();
        }
    
        void Update()
        {
            if( isGameOver == true )
            {
                if( Input.GetButtonDown(confirmButton) )
                {
                    Restart();
                }
            
                if( Input.GetButtonDown(pauseButton) )
                {
                    MainMenu();
                }
            }
            else
            {
                if( Input.GetButtonDown(pauseButton) )
                {
                    if( isPaused == true )
                        Unpause();
                    else
                        Pause();
                }

                if ( swipeControls == true )
                {
                    if ( swipeTimeoutCount > 0 )    swipeTimeoutCount -= Time.deltaTime;
                    
                    foreach ( Touch touch in Input.touches )
                    {
                        if ( touch.phase == TouchPhase.Began )
                        {
                            swipeStart = touch.position;
                            swipeEnd = touch.position;
                            
                            swipeTimeoutCount = swipeTimeout;
                        }
                        
                        if ( touch.phase == TouchPhase.Moved )
                        {
                            swipeEnd = touch.position;
                        }
                        
                        if( touch.phase == TouchPhase.Ended && swipeTimeoutCount > 0 )
                        {
                            if( (swipeStart.x - swipeEnd.x) > swipeDistance && (swipeStart.y - swipeEnd.y) < -swipeDistance )
                            {
                                MovePlayer("left");
                            }
                            else if((swipeStart.x - swipeEnd.x) < -swipeDistance && (swipeStart.y - swipeEnd.y) > swipeDistance )
                            {
                                MovePlayer("right");
                            }
                            else if((swipeStart.y - swipeEnd.y) < -swipeDistance && (swipeStart.x - swipeEnd.x) < -swipeDistance )
                            {
                                MovePlayer("forward");
                            }
                            else if((swipeStart.y - swipeEnd.y) > swipeDistance && (swipeStart.x - swipeEnd.x) > swipeDistance )
                            {
                                MovePlayer("backward");
                            }
                        }
                    }
                }
            }
            
            if( nextLanePosition - cameraObject.position.x < precreateLanes )
                CreateLane(1);
            
            if( cameraObject )
            {
                if( playerObjects[currentPlayer] )
                    cameraObject.position = new Vector3(Mathf.Lerp(cameraObject.position.x, playerObjects[currentPlayer].position.x, Time.deltaTime * 3), 0, Mathf.Lerp(cameraObject.position.z, playerObjects[currentPlayer].position.z, Time.deltaTime * 3));
            
                if( deathLineObject )
                {
                    Vector3 newVector3 = deathLineObject.position;

                    if( cameraObject.position.x > deathLineObject.position.x )
                        newVector3.x = cameraObject.position.x;

                    if( isGameOver == false )
                        newVector3.x += deathLineSpeed * Time.deltaTime;
            
                    deathLineObject.position = newVector3;
                }
            }
        }
    
        /// <summary>
        /// 创建小路
        /// </summary>
        /// <param name="laneCount"小路数量></param>
        void CreateLane(int laneCount)
        {
            while( laneCount > 0 )
            {
                laneCount--;
            
                int randomLane = Mathf.FloorToInt(Random.Range(0, lanesList.Length));
            
                Transform newLane = Instantiate(lanesList[randomLane].laneObject, new Vector3(nextLanePosition, 0, 0), Quaternion.identity) as Transform;

                if( Random.value < lanesList[randomLane].itemChance )
                { 
                    Transform newObject = Instantiate(objectDropList[Mathf.FloorToInt(Random.Range(0, objectDropList.Length))]) as Transform;

                    Vector3 newVector = new Vector3();

                    newVector = newLane.position;

                    newVector.z += Mathf.Round(Random.Range(-objectDropOffset, objectDropOffset));

                    newObject.position = newVector;
                }
            
                nextLanePosition += lanesList[randomLane].laneWidth;
            }
        }

        /// <summary>
        /// 改变分数
        /// </summary>
        /// <param name="changeValue">改变的分数</param>
        void ChangeScore(int changeValue)
        {

            score += changeValue;
        
            //更新文本的分数显示
            if( scoreText )
                scoreText.GetComponent<Text>().text = score.ToString();
        
            increaseCount += changeValue;
        
            //达到升级的条件,升级
            if( increaseCount >= levelUpEveryCoins )
            {
                increaseCount -= levelUpEveryCoins;
            
                LevelUp();
            }
        }

        /// <summary>
        /// 升级,并增加游戏难度
        /// </summary>
        void LevelUp()
        {
            //如果追杀物没有到达最大速度,增加它的速度
            if( deathLineSpeed + deathLineSpeedIncrease < deathLineSpeedMax )
                deathLineSpeed += deathLineSpeedIncrease;    

            //播放升级声音
            if( soundSourceTag != string.Empty && soundLevelUp )
                GameObject.FindGameObjectWithTag(soundSourceTag).GetComponent<AudioSource>().PlayOneShot(soundLevelUp);
        }
    
        /// <summary>
        /// 暂停游戏
        /// </summary>
        void Pause()
        {
            isPaused = true;
        
            Time.timeScale = 0;
        
            //显示暂停Canvas
            if( pauseCanvas )
                pauseCanvas.gameObject.SetActive(true);
            //隐藏游戏Canvas
            if( gameCanvas )
                gameCanvas.gameObject.SetActive(false);
        }
    
        /// <summary>
        /// 取消暂停
        /// </summary>
        void Unpause()
        {
            isPaused = false;
        
            Time.timeScale = gameSpeed;
        
            //隐藏暂停Canvas
            if( pauseCanvas )
                pauseCanvas.gameObject.SetActive(false);
            //显示游戏Canvas
            if( gameCanvas )
                gameCanvas.gameObject.SetActive(true);
        }
    
        /// <summary>
        /// 游戏结束协程
        /// </summary>
        /// <param name="delay">等待时间</param>
        IEnumerator GameOver(float delay)
        {
            yield return new WaitForSeconds(delay);
        
            isGameOver = true;
        
            //删除暂停面板
            if( pauseCanvas )
                Destroy(pauseCanvas.gameObject);
            //删除游戏面板
            if( gameCanvas )
                Destroy(gameCanvas.gameObject);

            //从存档中获取所有金币数量
            int totalCoins = PlayerPrefs.GetInt( coinsPlayerPrefs, 0);
            
            //添加本局游戏获得的得分
            totalCoins += score;
            
            //将新的总得分写入存档中
            PlayerPrefs.SetInt( coinsPlayerPrefs, totalCoins);
        
            if( gameOverCanvas )
            {
                //激活游戏结束面板
                gameOverCanvas.gameObject.SetActive(true);
            
                //显示本次游戏得分
                gameOverCanvas.Find("TextScore").GetComponent<Text>().text = "SCORE " + score.ToString();
            
                //设置最高得分
                if( score > highScore )
                {
                    highScore = score;
                
                    PlayerPrefs.SetInt(Application.loadedLevelName + "_HighScore", score);
                }
            
                //将最高得分显示在文本上
                gameOverCanvas.Find("TextHighScore").GetComponent<Text>().text = "HIGH SCORE " + highScore.ToString();
            }
        }
    
        /// <summary>
        /// 重新开始
        /// </summary>
        void Restart()
        {
            Application.LoadLevel(Application.loadedLevelName);
        }
    
        /// <summary>
        /// 回到主目录
        /// </summary>
        void MainMenu()
        {
            Application.LoadLevel(mainMenuLevelName);
        }

        /// <summary>
        /// 根据玩家编号,激活选择的玩家
        /// </summary>
        /// <param name="playerNumber"></param>
        void SetPlayer( int playerNumber )
        {
            for(index = 0; index < playerObjects.Length; index++)
            {
                if ( index != playerNumber )    
                    playerObjects[index].gameObject.SetActive(false);
                else    
                    playerObjects[index].gameObject.SetActive(true);
            }
        }

        /// <summary>
        /// 移动玩家
        /// </summary>
        /// <param name="moveDirection">移动方向</param>
        void MovePlayer( string moveDirection )
        {
            if ( playerObjects[currentPlayer] )    playerObjects[currentPlayer].SendMessage("Move", moveDirection);
        }
    }
}
RCGGameController
技术分享
using UnityEngine;

namespace RoadCrossing
{
    /// <summary>
    /// 小路
    /// </summary>
    public class RCGLane : MonoBehaviour
    {
        /// <summary>
        /// transform缓存
        /// </summary>
        internal Transform thisTransform;
    
        /// <summary>
        /// 起始位置
        /// </summary>
        public Vector3 laneStart = new Vector3(0, 0, -7);
        /// <summary>
        /// 结束位置
        /// </summary>
        public Vector3 laneEnd = new Vector3(0, 0, 7);
    
        /// <summary>
        /// 小路上候选的移动物体数组
        /// </summary>
        public Transform[] movingObjects;
    
        /// <summary>
        /// 小路上的移动物体数组
        /// </summary>
        internal Transform[] movingObjectsList;
    
        /// <summary>
        /// 小路上允许存在的移动物体的数量范围
        /// </summary>
        public Vector2 movingObjectsNumber = new Vector2(1, 3);

        /// <summary>
        /// 移动物体之间的间隔
        /// </summary>
        public float minimumObjectGap = 1;

        /// <summary>
        /// 所有物体的移动速度范围
        /// </summary>
        public Vector2 moveSpeed = new Vector2(0.5f, 1);
    
        /// <summary>
        /// 物体的移动方向
        /// </summary>
        internal int moveDirection = 1;

        /// <summary>
        /// 是否随机方向
        /// </summary>
        public bool randomDirection = true;
    
        void Start()
        {
            thisTransform = transform;
        
            //满足条件改变移动方向
            if( randomDirection == true && Random.value > 0.5f )
                moveDirection = -1;

            //随机选择移动速度
            moveSpeed.x = Random.Range(moveSpeed.x, moveSpeed.y);
        
            //计算路的长度
            float laneLength = Vector3.Distance(laneStart, laneEnd);
        
            //设置这条路上移动物体的数量
            int currentMovingObjectsNumber = Mathf.FloorToInt(Random.Range(movingObjectsNumber.x, movingObjectsNumber.y));
            
            //创建移动物体列表
            movingObjectsList = new Transform[currentMovingObjectsNumber];

            for(int index = 0; index < currentMovingObjectsNumber; index++)
            {
                //创建一个随机的移动物体
                Transform newMovingObject = Instantiate(movingObjects[Mathf.FloorToInt(Random.Range(0, movingObjects.Length))]) as Transform;
            
                //在小路起点放置这个物体
                newMovingObject.position = thisTransform.position + laneStart;
                //让这个物体朝向路的终点
                newMovingObject.LookAt(thisTransform.position + laneEnd);

                newMovingObject.Translate(Vector3.forward * index * laneLength / currentMovingObjectsNumber, Space.Self);

                newMovingObject.Translate(Vector3.forward * Random.Range(minimumObjectGap, laneLength / movingObjectsNumber.x - minimumObjectGap), Space.Self);

                //将这个物体添加到移动物体列表
                movingObjectsList[index] = newMovingObject;
            }

            foreach( Transform movingObject in movingObjectsList )
            {
                //根据方向设置移动物体的朝向,起点或终点
                if( moveDirection == 1 )
                    movingObject.LookAt(thisTransform.position + laneEnd);
                else
                    movingObject.LookAt(thisTransform.position + laneStart);
            
                //如果又动画组件,播放动画
                if( movingObject.GetComponent<Animation>() )
                {
                    //根据物体的移动速度设置动画的播放速度
                    movingObject.GetComponent<Animation>()[movingObject.GetComponent<Animation>().clip.name].speed = moveSpeed.x;
                }
            }
        }
    
        void Update()
        {
            foreach( Transform movingObject in movingObjectsList )
            {
                if( movingObject )
                {
                    //如果移动方向等于1
                    if( moveDirection == 1 )
                    {
                        //从起点向小路终点移动
                        movingObject.position = Vector3.MoveTowards(movingObject.position, thisTransform.position + laneEnd, moveSpeed.x * Time.deltaTime);
                    
                        //如果到达终点,将游戏物体放到起点
                        if( movingObject.position == thisTransform.position + laneEnd )
                        {
                            movingObject.position = thisTransform.position + laneStart;
                            movingObject.LookAt(thisTransform.position + laneEnd);
                        }
                    }
                    //如果移动方向不等于1
                    else
                    {
                        //从终点向小路起点移动
                        movingObject.position = Vector3.MoveTowards(movingObject.position, thisTransform.position + laneStart, moveSpeed.x * Time.deltaTime);
                    
                        //如果到达起点,将游戏物体放到终点
                        if( movingObject.position == thisTransform.position + laneStart )
                        {
                            movingObject.position = thisTransform.position + laneEnd;
                            movingObject.LookAt(thisTransform.position + laneStart);
                        }
                    }
                }
            }
        }
    
        /// <summary>
        /// Gizmos下画起始点和结束点
        /// </summary>
        void OnDrawGizmos()
        {
            Gizmos.color = Color.green;
            Gizmos.DrawSphere(transform.position + laneStart, 0.2f);
        
            Gizmos.color = Color.red;
            Gizmos.DrawSphere(transform.position + laneEnd, 0.2f);
        }
    }
}
RCGLane
技术分享
using UnityEngine;

namespace RoadCrossing
{
    /// <summary>
    /// 加载场景和URL
    /// </summary>
    public class RCGLoadLevel : MonoBehaviour
    {
        /// <summary>
        /// 加载URL
        /// </summary>
        /// <param name="urlName"></param>
        public void LoadURL(string urlName)
        {
            Application.OpenURL(urlName);
        }
    
        /// <summary>
        /// 加载指定场景
        /// </summary>
        /// <param name="levelName"></param>
        public void LoadLevel(string levelName)
        {
            Application.LoadLevel(levelName);
        }
        
        /// <summary>
        /// 重新加载当前场景
        /// </summary>
        public void RestartLevel()
        {
            Application.LoadLevel(Application.loadedLevelName);
        }
    }
}
RCGLoadLevel
技术分享
using UnityEngine;
using RoadCrossing.Types;

namespace RoadCrossing
{
    /// <summary>
    /// 玩家
    /// </summary>
    public class RCGPlayer : MonoBehaviour
    {
        /// <summary>
        /// 缓存transform
        /// </summary>
        internal Transform thisTransform;
        /// <summary>
        /// 游戏控制器
        /// </summary>
        internal GameObject gameController;
    
        /// <summary>
        /// 移动速度
        /// </summary>
        public float speed = 0.1f;
        /// <summary>
        /// 是否正在移动
        /// </summary>
        internal bool  isMoving = false;
        /// <summary>
        /// 上一个位置
        /// </summary>
        internal Vector3 previousPosition;
        /// <summary>
        /// 目标位置
        /// </summary>
        internal Vector3 targetPosition;

        /// <summary>
        /// 限制玩家移动的物体
        /// </summary>
        public Transform moveLimits;
        
        /// <summary>
        /// 移动动画
        /// </summary>
        public AnimationClip animationMove;
        /// <summary>
        /// 获得金币的动画
        /// </summary>
        public AnimationClip animationCoin;
    
        /// <summary>
        /// 死亡效果的动画列表
        /// </summary>
        public Transform[] deathEffect;
    
        /// <summary>
        /// 从不能移动到能移动的持续时间
        /// </summary>
        public float moveDelay = 0;
    
        /// <summary>
        /// 移动声音
        /// </summary>
        public AudioClip[] soundMove;
        /// <summary>
        /// 获得金币的声音
        /// </summary>
        public AudioClip[] soundCoin;

        /// <summary>
        /// AudioSource所属的游戏物体标签
        /// </summary>
        public string soundSourceTag = "GameController";
    
        void Start()
        {
            thisTransform = transform;

            targetPosition = thisTransform.position;
        
            gameController = GameObject.FindGameObjectWithTag("GameController");
        }
    
        void Update()
        {
            //让限制玩家移动的物体 跟随玩家移动
            if( moveLimits )
            {
                Vector3 newPosition = new Vector3();

                newPosition.x = thisTransform.position.x;

                moveLimits.position = newPosition;
            }

            //计算移动延迟
            if( moveDelay > 0 )
                moveDelay -= Time.deltaTime;
        
            //不处于移动状态 移动延迟<=0 时间缩放>0
            if( isMoving == false && moveDelay <= 0 && Time.timeScale > 0 )
            {
                //垂直抽没有输入值时,才可以左右移动
                if( Input.GetAxisRaw("Vertical") == 0 )
                {
                    //向右移动
                    if( Input.GetAxisRaw("Horizontal") > 0 )
                    {
                        Move("right");
                    }
                
                    //向左移动
                    if( Input.GetAxisRaw("Horizontal") < 0 )
                    {
                        // Move one step to the left
                        Move("left");
                    }
                }
            
                //水平轴没有输入值,才可以前后移动
                if( Input.GetAxisRaw("Horizontal") == 0 )
                {
                    //向前移动
                    if( Input.GetAxisRaw("Vertical") > 0 )
                    {
                        Move("forward");
                    }
                
                    //向后移动
                    if( Input.GetAxisRaw("Vertical") < 0 )
                    {
                        Move("backward");
                    }
                }
            }
            //其余的情况 移动玩家到目标位置
            else
            {
                //保持向前移动直到 到达目标位置
                if( thisTransform.position != targetPosition )
                {
                    //将玩家移动到目标位置
                    thisTransform.position = Vector3.MoveTowards(transform.position, targetPosition, Time.deltaTime * speed);
                }
                else
                {
                    //表示玩家不在移动状态中
                    isMoving = false;
                }
            }
        }
    
        /// <summary>
        /// 移动
        /// </summary>
        /// <param name="moveDirection">移动方向</param>
        void Move(string moveDirection)
        {
            if( isMoving == false && moveDelay <= 0 )
            {
                
                isMoving = true;
            
                switch( moveDirection.ToLower() )
                {
                    //向前移动
                    case "forward":
                        //让玩家朝前
                        Vector3 newEulerAngle = new Vector3();
                        newEulerAngle.y = 0;
                        thisTransform.eulerAngles = newEulerAngle;
                
                        //计算目标位置
                        targetPosition = thisTransform.position + new Vector3(1, 0, 0);
                
                        //让玩家在固定的网格里移动
                        targetPosition.x = Mathf.Round(targetPosition.x);
                        targetPosition.z = Mathf.Round(targetPosition.z);
                
                        //记录上一个位置,用来在遇到障碍物的时候,让玩家回到上一个位置
                        previousPosition = thisTransform.position;
                
                        break;
                
                    case "backward":
                        //让玩家朝后
                        newEulerAngle = new Vector3();
                        newEulerAngle.y = 180;
                        thisTransform.eulerAngles = newEulerAngle;

                        //记录上一个位置,用来在遇到障碍物的时候,让玩家回到上一个位置
                        previousPosition = thisTransform.position;

                        //让玩家在固定的网格里移动
                        targetPosition.x = Mathf.Round(targetPosition.x);
                        targetPosition.z = Mathf.Round(targetPosition.z);

                        //计算目标位置
                        targetPosition = thisTransform.position + new Vector3(-1, 0, 0);
                
                        break;
                
                    case "right":
                        //让玩家朝向右
                        newEulerAngle = new Vector3();
                        newEulerAngle.y = 90;
                        thisTransform.eulerAngles = newEulerAngle;

                        //记录上一个位置,用来在遇到障碍物的时候,让玩家回到上一个位置
                        previousPosition = thisTransform.position;

                        //让玩家在固定的网格里移动
                        targetPosition.x = Mathf.Round(targetPosition.x);
                        targetPosition.z = Mathf.Round(targetPosition.z);

                        //计算目标位置
                        targetPosition = thisTransform.position + new Vector3(0, 0, -1);
                
                        break;
                
                    case "left":
                        //让玩家朝向左
                        newEulerAngle = new Vector3();
                        newEulerAngle.y = -90;
                        thisTransform.eulerAngles = newEulerAngle;

                        //记录上一个位置,用来在遇到障碍物的时候,让玩家回到上一个位置
                        previousPosition = thisTransform.position;

                        //让玩家在固定的网格里移动
                        targetPosition.x = Mathf.Round(targetPosition.x);
                        targetPosition.z = Mathf.Round(targetPosition.z);

                        //计算目标位置
                        targetPosition = thisTransform.position + new Vector3(0, 0, 1);
                
                        break;
                
                    default:
                        //让玩家朝前
                        newEulerAngle = new Vector3();
                        newEulerAngle.y = 0;
                        thisTransform.eulerAngles = newEulerAngle;

                        //计算目标位置
                        targetPosition = thisTransform.position + new Vector3(1, 0, 0);
                        
                        //归一化目标位置
                        targetPosition.Normalize();

                        //记录上一个位置,用来在遇到障碍物的时候,让玩家回到上一个位置
                        previousPosition = thisTransform.position;
                
                        break;
                }
            
                //满足条件 播放移动动画
                if( GetComponent<Animation>() && animationMove )
                {
                    GetComponent<Animation>().Stop();
                    GetComponent<Animation>().Play(animationMove.name);
                
                    //基于移动速度设置动画速度
                    GetComponent<Animation>()[animationMove.name].speed = speed;
                
                    //满足条件播放移动声音
                    if( soundSourceTag != string.Empty && soundMove.Length > 0 )
                        GameObject.FindGameObjectWithTag(soundSourceTag).GetComponent<AudioSource>().PlayOneShot(soundMove[Mathf.FloorToInt(Random.value * soundMove.Length)]);
                }
            }
        }
    
        /// <summary>
        /// 取消玩家的移动,将玩家弹回到前一个位置
        /// </summary>
        void CancelMove(float moveDelayTime)
        {
            //满足条件播放动画
            if( GetComponent<Animation>() && animationMove )
            {
                //基于移动速度,倒放动画
                GetComponent<Animation>()[animationMove.name].speed = -speed;
            }
        
            //将目标位置设置为前一个位置
            targetPosition = previousPosition;
        
            //设置移动延迟
            moveDelay = moveDelayTime;
        }

        /// <summary>
        /// 获得金币
        /// </summary>
        void AddCoin(int addValue)
        {
            //调用游戏控制里的改变分数方法
            gameController.SendMessage("ChangeScore", addValue);
        
            //播放获得金币动画
            if( GetComponent<Animation>() && animationCoin && animationMove )
            {
                GetComponent<Animation>()[animationCoin.name].layer = 1; 
                GetComponent<Animation>().Play(animationCoin.name); 
                GetComponent<Animation>()[animationCoin.name].weight = 0.5f;
            }
        
            //播放获得金币声音
            if( soundSourceTag != string.Empty && soundCoin.Length > 0 )
                GameObject.FindGameObjectWithTag(soundSourceTag).GetComponent<AudioSource>().PlayOneShot(soundCoin[Mathf.FloorToInt(Random.value * soundCoin.Length)]);
        }
    
        /// <summary>
        /// 死亡
        /// </summary>
        /// <param name="deathType">死亡效果类型</param>
        void Die(int deathType)
        {
            //调用游戏控制器的游戏结束方法
            gameController.SendMessage("GameOver", 0.5f);
            
            //创建死亡特效
            if( deathEffect.Length > 0 )
            {
                Instantiate(deathEffect[deathType], thisTransform.position, thisTransform.rotation);
            }
        
            //销毁游戏物体
            Destroy(gameObject);
        }
    }
}
RCGPlayer
技术分享
using UnityEngine;

namespace RoadCrossing
{
    /// <summary>
    /// 播放声音
    /// </summary>
    public class RCGPlaySound : MonoBehaviour
    {
        /// <summary>
        /// 声音数组
        /// </summary>
        public AudioClip[] audioList;
    
        /// <summary>
        /// AudioSource所在物体的标签
        /// </summary>
        public string audioSourceTag = "GameController";
        /// <summary>
        /// 游戏开始时是否播放
        /// </summary>
        public bool playOnStart = true;
    
        void Start()
        {
            if( playOnStart == true )
                PlaySound();
        }
    
        /// <summary>
        /// 播放声音
        /// </summary>
        void PlaySound()
        {
            if( audioSourceTag != string.Empty && audioList.Length > 0 )
                GameObject.FindGameObjectWithTag(audioSourceTag).GetComponent<AudioSource>().PlayOneShot(audioList[Mathf.FloorToInt(Random.value * audioList.Length)]);
        }
    
        /// <summary>
        /// 根据索引播放声音
        /// </summary>
        void PlaySound(int soundIndex)
        {
            if( audioSourceTag != string.Empty && audioList.Length > 0 ) 
                GameObject.FindGameObjectWithTag(audioSourceTag).GetComponent<AudioSource>().PlayOneShot(audioList[soundIndex]);
        }
    }
}
RCGPlaySound
技术分享
using UnityEngine;

namespace RoadCrossing
{
    /// <summary>
    /// 随机化物体的颜色
    /// </summary>
    public class RCGRandomizeColor : MonoBehaviour
    {
        /// <summary>
        /// 颜色数组
        /// </summary>
        public Color[] colorList;
    
        void Start()
        {
            //随机选择一个颜色
            int randomColor = Mathf.FloorToInt(Random.Range(0, colorList.Length));
            //将颜色应用到物体和子物体上
            Component[] comps = GetComponentsInChildren<Renderer>();
            foreach( Renderer part in comps ) 
                part.GetComponent<Renderer>().material.color = colorList[randomColor];
        }
    }
}
RCGRandomizeColor
技术分享
using UnityEngine;

namespace RoadCrossing
{
    /// <summary>
    /// 随机化物体的旋转,缩放和颜色
    /// </summary>
    public class RCGRandomizeTransform : MonoBehaviour
    {
        /// <summary>
        /// x轴的旋转范围
        /// </summary>
        public Vector2 rotationRangeX = new Vector2(0, 360);
        /// <summary>
        /// y轴的旋转范围
        /// </summary>
        public Vector2 rotationRangeY = new Vector2(0, 360);
        /// <summary>
        /// z轴的旋转范围
        /// </summary>
        public Vector2 rotationRangeZ = new Vector2(0, 360);
    
        /// <summary>
        /// x轴缩放范围
        /// </summary>
        public Vector2 scaleRangeX = new Vector2(1, 1.3f);
        /// <summary>
        /// y轴缩放范围
        /// </summary>
        public Vector2 scaleRangeY = new Vector2(1, 1.3f);
        /// <summary>
        /// z轴缩放范围
        /// </summary>
        public Vector2 scaleRangeZ = new Vector2(1, 1.3f);
    
        /// <summary>
        /// 是否统一比例缩放
        /// </summary>
        public bool  uniformScale = true;
    
        /// <summary>
        /// 颜色列表
        /// </summary>
        public Color[] colorList;
    
        void Start()
        {
            //随机旋转
            transform.localEulerAngles = new Vector3(Random.Range(rotationRangeX.x, rotationRangeX.y), Random.Range(rotationRangeY.x, rotationRangeY.y), Random.Range(rotationRangeZ.x, rotationRangeZ.y));

            //如果是统一缩放,让y,z轴基于x轴的比例缩放
            if( uniformScale == true )
                scaleRangeY = scaleRangeZ = scaleRangeX;

            //随机缩放
            transform.localScale = new Vector3(Random.Range(scaleRangeX.x, scaleRangeX.y), Random.Range(scaleRangeY.x, scaleRangeY.y), Random.Range(scaleRangeZ.x, scaleRangeZ.y));

            //随机颜色
            int randomColor = Mathf.FloorToInt(Random.Range(0, colorList.Length));
            Component[] comps = GetComponentsInChildren<Renderer>();
            foreach( Renderer part in comps )
                part.GetComponent<Renderer>().material.color = colorList[randomColor];
        }
    }
}
RCGRandomizeTransform
技术分享
using UnityEngine;

namespace RoadCrossing
{
    /// <summary>
    /// 经过一段时间后删除物体,用来在特效播放完成后删除特效
    /// </summary>
    public class RCGRemoveAfter : MonoBehaviour
    {
        //删除物体的等待时间
        public float removeAfter = 1;
    
        void Update()
        {
            removeAfter -= Time.deltaTime;
        
            if( removeAfter <= 0 )
                Destroy(gameObject);
        }
    }
}
RCGRemoveAfter
技术分享
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using RoadCrossing.Types;

namespace RoadCrossing
{
    /// <summary>
    /// 商店
    /// </summary>
    public class RCGShop : MonoBehaviour
    {
        /// <summary>
        /// 剩余的金币数量
        /// </summary>
        public int coinsLeft = 0;
        
        /// <summary>
        /// 显示金币的文本
        /// </summary>
        public Transform coinsText;
        
        /// <summary>
        /// 玩家的金币值
        /// </summary>
        public string coinsPlayerPrefs = "Coins";

        /// <summary>
        /// 商店道具列表
        /// </summary>
        public ShopItem[] shopItems;

        /// <summary>
        /// 当前道具
        /// </summary>
        public int currentItem = 0;
        
        /// <summary>
        /// 存档里的当前玩家键
        /// </summary>
        public string playerPrefsName = "CurrentPlayer";
        
        /// <summary>
        /// 道具的未被选择时的颜色
        /// </summary>
        public Color unselectedColor = new Color(0.6f,0.6f,0.6f,1);
        
        /// <summary>
        /// 道具的选中颜色
        /// </summary>
        public Color selectedColor = new Color(1,1,1,1);
        
        /// <summary>
        /// 暂未使用
        /// </summary>
        public Color errorColor = new Color(1,0,0,1);

        void Start()
        {
            //获取剩余金币
            coinsLeft = PlayerPrefs.GetInt(coinsPlayerPrefs, coinsLeft);
            
            //更新金币文本
            coinsText.GetComponent<Text>().text = coinsLeft.ToString();
            
            //获取当前道具
            currentItem = PlayerPrefs.GetInt(playerPrefsName, currentItem);
            
            //更新所有道具
            UpdateItems();
        }

        /// <summary>
        /// 更新道具
        /// </summary>
        void UpdateItems()
        {
            for ( int index = 0 ; index < shopItems.Length ; index++ )
            {
                //从存档中获取这个道具的解锁状态
                shopItems[index].lockState = PlayerPrefs.GetInt(shopItems[index].playerPrefsName, shopItems[index].lockState);
                
                //设置道具的颜色为 未选择的颜色
                shopItems[index].itemButton.GetComponent<Image>().color = unselectedColor;
                
                //如果这个道具已经解锁
                if ( shopItems[index].lockState > 0 )
                {
                    //禁用显示价格的物体
                    shopItems[index].itemButton.Find("TextPrice").gameObject.SetActive(false);
                    
                    //高亮当前选择的物体的颜色
                    if ( index == currentItem )    shopItems[index].itemButton.GetComponent<Image>().color = selectedColor;
                }
                //如果没有解锁
                else
                {
                    //更新这个道具的价格
                    shopItems[index].itemButton.Find("TextPrice").GetComponent<Text>().text = shopItems[index].costToUnlock.ToString();
                }
            }
        }

        /// <summary>
        /// 买道具
        /// </summary>
        /// <param name="itemNumber"></param>
        void BuyItem( int itemNumber )
        {
            //如果这个道具已解锁,选择这个道具
            if ( shopItems[itemNumber].lockState > 0 )
            {
                SelectItem(itemNumber);
            }
            //如果有足够的金币,消耗金币买这个道具
            else if ( shopItems[itemNumber].costToUnlock <= coinsLeft )
            {
                //解锁道具
                shopItems[itemNumber].lockState = 1;
                
                //在存档中更新这个道具的状态
                PlayerPrefs.SetInt(shopItems[itemNumber].playerPrefsName, shopItems[itemNumber].lockState);
                
                //扣除金币
                coinsLeft -= shopItems[itemNumber].costToUnlock;
                
                //更新文本
                coinsText.GetComponent<Text>().text = coinsLeft.ToString();
                
                //将剩余金币写入Prefs
                PlayerPrefs.SetInt(coinsPlayerPrefs, coinsLeft);
                
                //选择道具
                SelectItem(itemNumber);
            }
            
            
            UpdateItems();
        }
        
        /// <summary>
        /// 选择道具
        /// </summary>
        /// <param name="itemNumber"></param>
        void SelectItem( int itemNumber )
        {
            currentItem = itemNumber;
            
            PlayerPrefs.SetInt( playerPrefsName, itemNumber);
        }
    }
}
RCGShop
技术分享
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

namespace RoadCrossing
{
    /// <summary>
    /// 显示文本
    /// </summary>
    public class RCGTextByPlatform : MonoBehaviour
    {

        /// <summary>
        /// 显示在PC/Mac/Webplayer中的文本
        /// </summary>
        public string computerText = "CLICK TO START";
    
        /// <summary>
        /// 显示在Android/iOS/WinPhone中的文本
        /// </summary>
        public string mobileText = "TAP TO START";
    
        /// <summary>
        /// 显示在Playstation/Xbox/Wii中的文本
        /// </summary>
        public string consoleText = "PRESS ‘A‘ TO START";
    
        void Start()
        {
            #if UNITY_STANDALONE || UNITY_WEBPLAYER
            GetComponent<Text>().text = computerText;
            #endif
        
            #if IPHONE || ANDROID || UNITY_BLACKBERRY || UNITY_WP8 || UNITY_METRO
            GetComponent<Text>().text = mobileText;
            #endif
        
            #if UNITY_WII || UNITY_PS3 || UNITY_XBOX360
            GetComponent<Text>().text = consoleText;
            #endif
        }
    }
}
RCGTextByPlatform
技术分享
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

namespace RoadCrossing
{
    /// <summary>
    /// 声音开关
    /// </summary>
    public class RCGToggleSound:MonoBehaviour
    {
        /// <summary>
        /// 播放声音的物体
        /// </summary>
        public Transform soundObject;
    
        /// <summary>
        /// 存档中的名称
        /// </summary>
        public string playerPref = "SoundVolume";
    
        /// <summary>
        /// 声音的当前音量
        /// </summary>
        internal float currentState = 1;
    
        void Awake()
        {
            if( soundObject )
                currentState = PlayerPrefs.GetFloat(playerPref, soundObject.GetComponent<AudioSource>().volume);
            else   
                currentState = PlayerPrefs.GetFloat(playerPref, currentState);
        
            SetSound();
        }
    
        /// <summary>
        /// 设置声音大小
        /// </summary>
        void SetSound()
        {
            //设置声音存档
            PlayerPrefs.SetFloat(playerPref, currentState);

            Color newColor = GetComponent<Image>().material.color;

            //根据声音存档值设置按钮透明度
            if( currentState == 1 )
                newColor.a = 1;
            else
                newColor.a = 0.5f;

            //应用透明度
            GetComponent<Image>().color = newColor;

            //设置音量
            if( soundObject ) 
                soundObject.GetComponent<AudioSource>().volume = currentState;
        }
    
        /// <summary>
        /// 开关声音
        /// </summary>
        void ToggleSound()
        {
            currentState = 1 - currentState;
        
            SetSound();
        }
    
        /// <summary>
        /// 播放声音
        /// </summary>
        void StartSound()
        {    
            if( soundObject )
                soundObject.GetComponent<AudioSource>().Play();
        }
    }
}
RCGToggleSound

视频:https://pan.baidu.com/s/1qYPhpsK

项目:https://pan.baidu.com/s/1mi5C7s0

Road Crossing Game Template 学习