unity 贪吃蛇作战—1

   前段时间看见有一个游戏比较火,一直没有时间模仿着写,最近空闲 抽出几天时间写写试试吧。做出来的只能算是一个demo吧。贪吃蛇的原理应该很简单吧,基本说一下,就是当蛇动起来的时候,蛇头的位置被重新赋值,蛇的第二段赋原先蛇头的位置,第三段赋原先第二段的的位置。所以原理其实很简单。就是一个数组每帧对数组进行修改,然后新对蛇段位置重新赋值。下面先给几张图片





该项目只能算一个demo因为有好多细节的东西没有做,比如接sdk,其他的ui的一些设置都没有,目前只有一个模式,游戏分为玩家蛇和一些AI蛇,所以我们需要建2个类他们分别为PlayerSnake和AISanke,同时他们都有一些共同点,他们的移动方式是一样的都是蛇头先赋值 然后蛇身依次根据前面一个蛇身的位置进行赋值。并且他们都有蛇头和蛇身之分,只是他们的控制方式不同,具体的讲就是他们得到蛇头的移动方向方式不同,一种是通过虚拟摇杆来控制蛇头的移动方向,另外一种是通过具体的AI的控制蛇头的移动方向。所以玩家蛇应该来说比较好写,所以我们需建一个BaseSnake 让玩家蛇和AI蛇都继承BaseSnake,BaseSnake里面就应该只有蛇的运动原理及一些基本的属性和字段了。先贴出BaseSnake代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Snake
{
    public abstract class BaseSnake : MonoBehaviour,IComparable
    {

        public GameObject SnakeHead { set; get; }

        public float SpeeRatio { set; get; }

        public string NickName { set; get; }

        public int SnakeIndex { set; get; }

        public List<GameObject> SnakeNodes { set; get; }

        public int Count { get { return SnakeNodes.Count; } }

        private List<Vector3> _wayPoints;

        public Vector3 MoveDir;

        #region protected Variable
        
        protected Vector3 _tempDir;
        protected RaycastHit _hit;

        protected int _snakeNodeCount;
        protected int _snakeSkin;
        protected SkinsTable.SkinItem _skinItem;
        protected int _eatFoodCount;

        #endregion

        public virtual void OnAwake()
        {
            SnakeNodes=new List<GameObject>();
            _wayPoints=new List<Vector3>();

            SpeeRatio = 1;
            NickName = GetStr.GetNickName();
        }

        public virtual void OnUpdate()
        {
            
        }
       

        #region Private Method

        private void AddWayPoint(Vector3 point)
        {
            _wayPoints.Add(point);
        }

        private void AddNodeObj(GameObject obj)
        {
            SnakeNodes.Add(obj);
        }

        private void AddSnakeNodes(int count, Vector3 originVector3)
        {
            Vector3 originPos = originVector3;
            for (int i = 0; i < count; i++)
            {
                if (i <= count - 1)
                {
                    GenerateBlankBody(originPos);
                }

                var body = new GameObject("Body", typeof(SpriteRenderer));

                body.tag = "Body";
                body.layer = LayerMask.NameToLayer("Body");
                body.transform.position = originVector3 - 0.2f * MoveDir.normalized * (i + 1);
                originPos = originVector3 - 0.2f * MoveDir.normalized * (i + 1);

                if (_skinItem.Body1Sprites != null && _skinItem.Body2Sprites != null)
                    body.GetComponent<SpriteRenderer>().sprite = i % 2 == 0 ? _skinItem.Body1Sprites : _skinItem.Body2Sprites;
                else
                {
                    body.GetComponent<SpriteRenderer>().sprite = _skinItem.Body1Sprites;
                }

                body.AddComponent<CircleCollider2D>().isTrigger = true;
                body.AddComponent<BodyIndex>().ParentSnakeIndex = SnakeIndex;

                float value = MathTool.GetAngleByVector(new Vector2(MoveDir.x, MoveDir.y));
                body.transform.eulerAngles = new Vector3(0, 0, value - 90);
                body.transform.SetParent(transform);

                AddNodeObj(body);
                AddWayPoint(body.transform.position);

            }
        }

        private void GenerateBlankBody(Vector3 originVector3)
        {
            Vector3 pos = Vector3.zero;
            for (int i = 1; i < 4; i++)
            {
                pos = originVector3 - MoveDir.normalized * 0.2f / 4f * i;
                AddWayPoint(pos);
            }
        }

        private void MoveToTargetPos(Vector3 targetPos)
        {
            for (int i = _wayPoints.Count - 1; i >= 1; i--)
            {
                _wayPoints[i] = _wayPoints[i - 1];
            }
            _wayPoints[0] = new Vector3(targetPos.x, targetPos.y, 0);
            for (int i = 0; i < SnakeNodes.Count; i++)
            {
                SnakeNodes[i].transform.position = _wayPoints[i * 4];
            }

        }

        #endregion


        #region Public Method

        public virtual  void IntilizedSnakePos(Vector3 pos, Vector3 direction,int snakeIndex)
        {
            transform.position = pos;
            MoveDir = direction;
            SnakeIndex = snakeIndex;
        }

        public virtual void IntilizedSnakeNodes(int originCount, int skinIndex,string nickName=null)
        {
            OnAwake();
            _skinItem = SkinsTable.Instance.GetItemByIndex(skinIndex);
            SnakeHead = new GameObject("SnakeHead", typeof(SpriteRenderer));

            SnakeHead.tag = "Head";
            SnakeHead.layer = LayerMask.NameToLayer("Body");

           

            SnakeHead.transform.SetParent(transform);
            SnakeHead.transform.localPosition = Vector3.zero;
            SnakeHead.GetComponent<SpriteRenderer>().sprite = _skinItem.HeadSprite;
            SnakeHead.AddComponent<Rigidbody2D>().gravityScale = 0;
            var circleColider = SnakeHead.AddComponent<CircleCollider2D>();
            circleColider.isTrigger = true;
            circleColider.radius = 0.13f;
            SnakeHead.AddComponent<BodyIndex>().ParentSnakeIndex = SnakeIndex;
            float angle = MathTool.GetAngleByVector(new Vector2(MoveDir.x, MoveDir.y));
            SnakeHead.transform.eulerAngles = new Vector3(0, 0, angle - 90);


            Vector3 tempPos = SnakeHead.transform.position;

            AddNodeObj(SnakeHead);
            AddWayPoint(SnakeHead.transform.position);
            AddSnakeNodes(originCount, tempPos);
        }

        public void EatFood(GameObject food)
        {

            var foodColor = food.GetComponent<FoodColor>();
            _eatFoodCount += foodColor.Weight;
            int count = _eatFoodCount / 4;
            _eatFoodCount %= 4;
            if (count != 0)
            {
                AddSnakeNodes(count);
            }


            if (foodColor.Weight <= 1)
            {

                foodColor.IntilizedColor();
                foodColor.IntilizedWeight(1);

                float x = Random.Range(-13f, 13f);
                float y = Random.Range(-7f, 7f);
                food.transform.localPosition = new Vector3(x, y, 0);
            }
            else
            {
                Destroy(food);
            }
           
        }

        #endregion


        #region Protected Method

        protected void RotateHead()
        {
            float value = MathTool.GetAngleByVector(new Vector2(MoveDir.x, MoveDir.y));
            SnakeHead.transform.eulerAngles = new Vector3(0, 0, value - 90);
            Vector3 tempPos = SnakeHead.transform.position;
            for (int i = 1; i < SnakeNodes.Count; i++)
            {
                Vector3 dir = (tempPos - SnakeNodes[i].transform.position);
                var angle = MathTool.GetAngleByVector(new Vector2(dir.x, dir.y));
                SnakeNodes[i].transform.eulerAngles = new Vector3(0, 0, angle);
            }

        }

        protected void MoveSnake()
        {
            for (int i = 0; i < SpeeRatio; i++)
            {
                Vector3 endPos = Vector3.zero;
                if (SnakeNodes[0] != null)
                    endPos = SnakeNodes[0].transform.position + 0.2f / 4f * MoveDir.normalized;
                MoveToTargetPos(endPos);
            }
        }

        protected void AddSnakeNodes(int count)
        {
            Vector3 tempPos = SnakeNodes[SnakeNodes.Count - 1].transform.position;
            AddSnakeNodes(count, tempPos);
        } 

        #endregion

        public int CompareTo(object obj)
        {
            var snake = obj as BaseSnake;
            if (this.Count > snake.Count)
                return 1;
            if (this.Count == snake.Count)
                return 0;
            if (this.Count < snake.Count)
                return -1;
            return -1;
        }
    }
}
总共代码就200多行,这些私有方法就不用看了,只需看看Public方法和一些Protect方法了,首先让2个有关生命周期的方法OnAwake和OnUpdate方法虚化,目的是让他的2个子类去重写它,另外有2个初始化的方法也被虚化了,其实IntilizedSnakePos这个方法没有虚化的必要性,因为在他们的子类中都没有重写,这样写的原因只是为了看起来比较好看一些。另外还有一个EatFood,这个方法玩家蛇和AI蛇都应该具有的行为。至于RotateHead,MoveSnake和AddSnakeNodes都是一些关于蛇移动处理和蛇身添加处理的方法,RotateHead主要旋转蛇头并且旋转蛇身的方向。MoveSnake主要是移动整条蛇。最后一个方法纯粹就是给蛇添加蛇身。旋转蛇头涉及一个简单的数学知识。这里可以简单的讲讲,就是我们再二维坐标中随便给出一个向量,我们就知道它的方向,例如(1,0)我们知道它对应得旋转量为(0,0,0),(0,1)对应得旋转量为(0,0,90),所以我们的根据具体的向量来计算出蛇头的朝向问题。首先我们需要让蛇头的移动向量和(1,0)做点乘,然后通过点乘求得一个角度,这个角度在0到180度之间,因为点乘的求得只是夹角。例如向量(0,1)我们求得夹角为90,那么它对应的欧拉角可能是(0,0,90)和(0,0,-90)了,所以我们得从其中选取正确的一个值,这是我们再做一下简单的叉乘,用叉乘求出来一个向量,判断向量实在朝上还是朝下,如果是朝上说明正90,相反就是-90了,具体方法如下

 public static float GetAngleByVector(Vector2 dir)
        {
            Vector2 relativeVector = new Vector2(1, 0);
            float value = Vector2.Dot(dir.normalized, relativeVector);
            float angle = Mathf.Acos(value) * Mathf.Rad2Deg;

            float value1 = relativeVector.x*dir.y - relativeVector.y*dir.x;
            angle *= value1 > 0 ? 1 : -1;
            return angle;
        }
游戏虽然是很简单,但这样说的目的只是在怎么去组织自己的代码。下节继续把这个demo说完。



  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值