• 回复@大雨582:社会人不可能像自然人一样自由,你还得尊重别人的意愿!不然咱随便生产啥都强行让你要,咱是自由了,你呢? 2019-10-12
  • 发改委每月首个工作日公布限制乘坐火车飞机人员名单 2019-10-12
  • 全国啦啦操联赛临汾站开幕 2019-10-08
  • 《爱疯音乐家》陈洁仪:不完美的美其实更美爱疯音乐家陈洁仪 2019-10-08
  • 中冶置业兴隆百万平米新城正式亮相 将打造全配套高铁康养小镇 2019-10-06
  • 以政府拟严打“乱拍摄”以军行为 最高10年监禁 2019-10-03
  • 图解:关于中国梦,习近平总书记这十句话直抵人心 2019-09-20
  • “舌尖”之后, “风味”能否再续辉煌? 2019-09-17
  • 腾讯要花32亿收购《绝地求生》开发商10%股份 2019-09-17
  • 涨知识!文字里的秘密 最全甲骨文书展来了 2019-09-13
  • 晋中市着力将“厕所革命”进行到底 2019-09-05
  • 供应链如何更“智慧” 2019-09-05
  • 红枣食疗效果好 6种吃法最滋补-美食资讯 2019-09-01
  • 党的十九大最重大的理论成就 2019-08-28
  • 余世存:阅读经典不仅是消费,也是责任 2019-08-20
  • 南粤风采今天开奖公告: 士郎 用Unity开发一款塔防游戏(一):攻击方设计

    27
    回复
    2225
    查看
    打印 上一主题 下一主题
    [ 复制链接 ]
    排名
    1
    昨日变化

    福建36选7和值走势图 www.00-na.com 8235

    主题

    8793

    帖子

    3万

    积分

    Rank: 16

    UID
    1231
    好友
    186
    蛮牛币
    13057
    威望
    30
    注册时间
    2013-7-29
    在线时间
    4205 小时
    最后登录
    2019-10-19

    活力之星原创精华达人突出贡献奖财富之证游戏蛮牛QQ群会员蛮牛妹VIP

    马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

    您需要 登录 才可以下载或查看,没有帐号?注册帐号

    x
    塔防游戏相信大家并不陌生,几个主要元素如下:

    1、敌方士兵

    2、我方防御塔

    3、我方主城

    emmmmmmm好像就没了。


    人越狠,话越不多。不多说,接下来我们一步步把这几个功能做完。

    素材准备:

    网上随便找一些资源就行,不一定要和我一样。这里再次强调:

    网上获取的资源一定不能用作商业用途?。。。。?!

    就本工程而言,资源有一下几种:

    敌人2个,分别拥有移动,攻击,待机,死亡四种动画



    防御塔3个,拥有待机,攻击两种动画


    人形防御塔可还行

    主城1个,主地形1组(内含各种杂草乱石)



    敌人地形(敌人能用来走的路)1种,防御塔地形(防御塔能放置的地方)1种,箭矢1个


    场景搭建:

    先从简单的功能做起:让敌人从生成点走到主城,看见主城就攻击。

    搭建一个简单场景:


    为了检测敌人寻路,最好是能转弯的道路

    敌人和主城有一个都有血量的属性,都会被攻击,这里为它们做能显示在头上的血条。

    以主城为例,在主城的子节点层创建一个Sprite做黄血条,设为黄色,取名“BloodStrip”,调整好大?。?/font>



    然后在BloodStrip的子节点层创建一个空物体,取名“Hp”,在Hp的子节点层再创建一个Sprite做红血条,名字“Red”,设为红色,大小和黄血条一样,把黄血色覆盖:



    接下来就移动红血条位置,让它左边边缘与父物体Hp的Y轴重合:



    然后再将Hp往右移动,让Y轴与黄血条左边缘重合(红血条刚好覆盖黄血条):



    这样我们只需要设置H的X轴大小,就可以控制红血条长度了


    ***这里请初学者注意,如果你选取的红血条图片资源不是纯色的、是有其他花纹的,则不能用这个方法。原因很简单,这种方法会把花纹拉长或压扁。大家可以下来想一下:这种情况下应该怎样来设置?

    后面在代码中只需要将当前血量与总血量的比值赋给Hp的X轴,就可以将血量信息显示在界面上了。敌人血条做法一样。

    做好后让BloodStrip处于禁用状态,受伤后才显示(这是游戏UI显示的一个约定俗成的规则)。

    代码编写:

    为主城与敌人创建一个基类脚本Character:

    [AppleScript] 纯文本查看 复制代码
    public class Character : MonoBehaviour
    {
        public float totalHp = 100; //总血量
        float surHp; //剩余血量
        protected Transform hpObj; //黄血条
        protected Transform redHp; //血条红条
        protected Transform mainCamera; //主摄像机
    
        public virtual void Init() //初始化
        {
            surHp = totalHp;
            hpObj = transform.Find("BloodStrip");
            redHp = hpObj.Find("Hp");
            mainCamera = GameObject.Find("Main Camera").transform;
        }
        public void Damage(float damage) //受伤方法,参数为受到的伤害值
        {
            if (surHp > damage) //当前血量大于受伤血量,正??垩?
            {
                surHp -= damage;
                //受伤后开始显示血条
                if (surHp < totalHp)
                    hpObj.gameObject.SetActive(true);
                Vector3 hpScale = redHp.localScale;
                hpScale.x = surHp / totalHp;
                redHp.localScale = hpScale;
            }
            else //当前血量不够,调用死亡方法          
                Death();
        }
        public virtual void Death() //死亡方法
        {
            surHp = 0;
            hpObj.gameObject.SetActive(false); //血条不再显示
        }
    }


    创建主调脚本:用于游戏初始化和记录游戏死亡,挂在一个场景物体上:


    [AppleScript] 纯文本查看 复制代码
    public class GameMain : MonoBehaviour
    {
        public static GameMain instance;
        public bool gameOver;
        void Start()
        {
            InitGame();
        }
        //初始化游戏
        void InitGame()
        {
            instance = this; //单例
            gameOver = false;
        }
    }


    创建主城脚本,继承自Character脚本:
    [AppleScript] 纯文本查看 复制代码
    public class MainCity : Character
    {
        void Start()
        {
            Init();
        }
        private void Update()
        {
            hpObj.rotation = mainCamera.rotation; //血条始终面向镜头
        }
        public override void Death() //重新死亡方法
        {
            base.Death();
            GameMain.instance.gameOver = true; //游戏结束
        }
    }
    


    敌人的脚本也继承自Charater,除了受伤和死亡之外还能攻击与移动:
    [AppleScript] 纯文本查看 复制代码
    public class Enemy : Character
    {
        Animator anim;
        public float damage; //伤害
        public float speed; //移动速度
        MainCity target; //主城
        public override void Init()
        {
            base.Init();
            anim = GetComponent<Animator>();
        }
        private void Update()
        {
            hpObj.rotation = mainCamera.rotation; //血条始终面向镜头
        }
        //前进方法
        private void EnemyForward()
        {
        }
        //攻击方法(放在攻击动画事件中)
        private void EnemyAttack()
        {
            if (target != null)
                target.Damage(damage);
        }
        //死亡方法
        public override void Death()
        {
            base.Death();
            anim.Play("death");
        }
        //尸体消失
        private void DestroySelf()
        {
            Destroy(gameObject);
        }
    }


    重点在移动方法上。因为敌人的移动带有寻路功能,这里没有采取Unity自带的NavMeshAgent,而是用脚本来实现,主要思路仿照盲人的行进方式,利用射线充当导盲棍,发现前方道路中断再从两边找新的行进路线:


    拐杖就是射线

    要利用好这个思路,场景中道路的搭建也有一定要求,道路都要挂上MeshCollider组件,方便射线检测。


    所有道路的Z轴指向路线前进方向

    道路的物体层设置为“Way”,主城也挂上碰撞器,物体层设为“City”。



    在敌人模型身上创建一个空物体为眼睛,取名为“Eye”,主要作用是从此为射线起始点,位置合适即可,注意,因为所有敌人都用的相同脚本,所以所有敌人的眼睛高度距离地面相同:


    正面看这些模型真特么惊悚

    当然每个敌人也请挂上碰撞器和刚体以及Animator组件:



    创建一个敌人状态机:
    [AppleScript] 纯文本查看 复制代码
    public enum EnemyState //状态机
    {
        forward,
        attack,
        death
    }


    重写初始化方法:
    [AppleScript] 纯文本查看 复制代码
     Animator anim;
        Rigidbody rigid;
        public EnemyState state;
        Transform eye; //眼睛:用于观测道路和攻击目标
        List<Collider> ways; //记录走过的路(不走回头路)
        //重新初始化方法
        public override void Init() 
        {
            base.Init();
        
            anim = GetComponent<Animator>();
            rigid = GetComponent<Rigidbody>();
            gameObject.layer = LayerMask.NameToLayer("Enemy"); //敌人层设置为"Enemy"
            state = EnemyState.forward;
            eye = transform.Find("Eye");
            ways = new List<Collider>();
        }


    编写移动方法,并在Update中调用:
    [AppleScript] 纯文本查看 复制代码
    private void Update()
        {
            hpObj.rotation = mainCamera.rotation; //血条始终面向镜头
            if (GameMain.instance.gameOver) //游戏结束播放待机动画
                anim.Play("idle");
            else if (state == EnemyState.forward)
                EnemyForward();
        }
        public int view; //视野
        Quaternion wayDir; //前进方向
        MainCity target; //主城
        Transform way; //正在走的路
        public float speed; 
        //前进方法
        private void EnemyForward()
        {
            RaycastHit hit;
            //看见攻击目标则攻击
            if (Physics.Raycast(eye.position, transform.forward, out hit, view, LayerMask.GetMask("City")))
            {
                state = EnemyState.attack;
                anim.Play("attack");
                target = hit.collider.GetComponent<MainCity>();
            }
    
            //斜下方30°打射线检测前方道路
            if (Physics.Raycast(eye.position, Quaternion.AngleAxis(30, transform.right)
                * transform.forward, out hit, 50, LayerMask.GetMask("Way")))
            {
                Debug.DrawLine(eye.position, hit.point, Color.blue);
                //发现未走过的道路,获取该道路,朝向该路通往的方向
                if (!ways.Contains(hit.collider))
                {
                    ways.Add(hit.collider);
                    way = hit.transform;
                    wayDir = Quaternion.LookRotation(way.forward);
                }
            }
            else //前方没路了发射球形射线检测周围是否有路
            {
                Collider[] colliders = Physics.OverlapSphere(transform.position, 8, LayerMask.GetMask("Way"));
                for (int i = 0; i < colliders.Length; i++)
                {
                    //发现未走过的道路,获取该道路,朝向该路通往的方向
                    if (!ways.Contains(colliders[i]))
                    {
                        way = colliders[i].transform;
                        wayDir = Quaternion.LookRotation(way.forward);
                        break;
                    }
                }
            }
            //获取与脚下道路x轴上偏差值,好让自身走在路中间
            float offset = 0;
            if (way != null)
            {
                Vector3 distance = transform.position - way.position;
                offset = Vector3.Dot(distance, way.right.normalized);
            }
            //面向该路指向的方向前进
            transform.rotation = Quaternion.RotateTowards(transform.rotation, wayDir, speed * 20 * Time.deltaTime);
            transform.Translate(-offset * Time.deltaTime, 0, speed * Time.deltaTime);
        }


    暂时把初始化方法放在Start中调用(后面我们会在创建的时候初始化),然后设置好血量、视野、速度、伤害,主城也设置好血量:



    先来看下寻路运行效果:


    蓝线检测前方道路,红圈检测周围道路

    寻路没有问题了,将攻击动画设为循环播放,然后将攻击方法放入攻击动画事件中,敌人看到主城就会自动攻击了:

    敌人主要功能就已经完成。现在我们来做敌人生成器。

    塔防游戏的敌人生成方式一般都是比较有规律的,比如先生成一组a敌人,跟着生成一组b敌人,每组敌人的生成间隔也恒定(当然,读者也可以自己尝试更丰富的出兵方法,比如让“某些特定敌人的血量减到某个阈值”作为触发条件等等):



    为了生成方便,我们来做一个定时器,可以重复并规律地调用一个生成敌人方法:
    [AppleScript] 纯文本查看 复制代码
    public class Util : MonoBehaviour
    {
        private static Util _Instance = null;
        public static Util Instance //单例模式,依附GameObject
        {
            get
            {
                if (_Instance == null)
                {
                    GameObject obj = new GameObject("Util");
                    _Instance = obj.AddComponent<Util>();
                }
                return _Instance;
            }
        }
        public class TimeTask //定时事件类
        {
            public Action callback; //回调函数
            public float delayTime; //延迟长度
            public float destTime; //延迟后的目标时间
            public int count; //重复次数
        }                
        List<TimeTask> timeTaskList = new List<TimeTask>(); //保存所有的定时事件   
        //增加定时回调的方法 
        public void AddTimeTask(Action _callback, float _delayTime, int _count = 1)     
        {
            timeTaskList.Add(new TimeTask()
            {
                callback = _callback,
                delayTime = _delayTime,
                destTime = Time.realtimeSinceStartup + _delayTime,
                count = _count
            });
        }
        private void Update()
        {
            for (int i = 0; i < timeTaskList.Count; i++) //实时监测所有定时事件
            {
                TimeTask task = timeTaskList[i];
                if (Time.realtimeSinceStartup >= task.destTime) //时间到了,则执行
                {
                    task.callback?.Invoke(); 
                    if (task.count == 1) //当次数为1,执行完移除该定时事件
                        timeTaskList.RemoveAt(i);
                    else if (task.count > 1) //当次数大于1,执行完次数减1
                        task.count--;
                    task.destTime += task.delayTime; //执行完一次后,重新定出下次执行时间
                }
            }
        }
    }


    把所有敌人放入一个路径中,创建一个空物体做敌人生成器,放在敌人生成点,创建脚本挂上去:
    [AppleScript] 纯文本查看 复制代码
    public class EnemySystem : MonoBehaviour
    {
        //根据名称保存所有敌人
    Dictionary<string, Enemy> enemyDict = new Dictionary<string, Enemy>();
    //初始化,放在主调脚本GameMain中执行
        public void Init()
        {
            //保存所有种类敌人,可以根据名字获取
            Enemy[] enemys = Resources.LoadAll<Enemy>("Prefab/Chara/EnemyChara");
            for (int i = 0; i < enemys.Length; i++)
            {
                if (!enemyDict.ContainsKey(enemys[i].name))
                    enemyDict.Add(enemys[i].name, enemys[i]);
            }
        }
        //生成敌人,参数中设置敌人种类,生成间隔,生成数量(默认为1)
        public void CreateEnemy(string name, float delay, int count = 1)
        {
            if (GameMain.instance.gameOver == false)
                //使用定时器,生成敌人
                Util.Instance.AddTimeTask(() => Instantiate(
                enemyDict[name], transform.position, transform.rotation).Init(),
                delay, count);
    }
        //点击按钮生成敌人(挂在按钮事件中)
        public void ClickButtonDispatchTroops()
        {
            //每秒生成一个敌人,生成5次,第一次生成在1秒后执行
            CreateEnemy("Zombie1", 1, 5); 
            //没0.5秒生成一个敌人,生成10次,第一次生成在5.5秒后执行
            Util.Instance.AddTimeTask(() => CreateEnemy("Zombie2", 0.5f, 10), 5);
        }
    }


    做到这一步就可以像演示视频中那样点击按钮出兵了。

    放上工程链接:

    https://pan.baidu.com/s/1T2nZ_FrIk9DaTvem-YH8nQ提取码:n61s

    知乎@四五二十

    0

    主题

    11

    帖子

    52

    积分

    Rank: 2Rank: 2

    UID
    299963
    好友
    0
    蛮牛币
    76
    威望
    0
    注册时间
    2018-10-14
    在线时间
    41 小时
    最后登录
    2019-8-30
    沙发
    2019-6-12 21:20:04 只看该作者
    非常感谢楼主,刚好缺这方面的姿势
    7日久生情
    2332/5000
    排名
    4093
    昨日变化

    0

    主题

    1578

    帖子

    2332

    积分

    Rank: 7Rank: 7Rank: 7Rank: 7

    UID
    254705
    好友
    1
    蛮牛币
    2097
    威望
    0
    注册时间
    2017-11-16
    在线时间
    392 小时
    最后登录
    2019-10-18
    板凳
    2019-6-13 08:23:17 只看该作者
    66666666666666666666666666666666
    4四处流浪
    381/500
    排名
    8824
    昨日变化

    0

    主题

    39

    帖子

    381

    积分

    Rank: 4

    UID
    307305
    好友
    0
    蛮牛币
    870
    威望
    0
    注册时间
    2018-12-6
    在线时间
    208 小时
    最后登录
    2019-10-19
    地板
    2019-6-13 08:38:06 只看该作者
    感谢分享
    7日久生情
    1894/5000
    排名
    1395
    昨日变化

    15

    主题

    274

    帖子

    1894

    积分

    Rank: 7Rank: 7Rank: 7Rank: 7

    UID
    119664
    好友
    3
    蛮牛币
    8321
    威望
    0
    注册时间
    2015-8-25
    在线时间
    725 小时
    最后登录
    2019-10-16
    QQ
    5#
    2019-6-13 09:01:41 只看该作者
    学习学习~~~~~~~~~~~~~~~
    6蛮牛粉丝
    1023/1500
    排名
    16957
    昨日变化

    1

    主题

    795

    帖子

    1023

    积分

    Rank: 6Rank: 6Rank: 6

    UID
    175005
    好友
    0
    蛮牛币
    984
    威望
    0
    注册时间
    2016-10-12
    在线时间
    187 小时
    最后登录
    2019-9-29
    6#
    2019-6-13 09:11:50 只看该作者
    不错
    4四处流浪
    488/500
    排名
    7196
    昨日变化

    0

    主题

    143

    帖子

    488

    积分

    Rank: 4

    UID
    267358
    好友
    0
    蛮牛币
    659
    威望
    0
    注册时间
    2018-2-1
    在线时间
    165 小时
    最后登录
    2019-9-29
    7#
    2019-6-13 09:53:00 只看该作者
    学习大佬操作
    4四处流浪
    423/500
    排名
    16595
    昨日变化

    1

    主题

    137

    帖子

    423

    积分

    Rank: 4

    UID
    281418
    好友
    0
    蛮牛币
    692
    威望
    0
    注册时间
    2018-5-16
    在线时间
    243 小时
    最后登录
    2019-10-19
    8#
    2019-6-13 10:17:34 只看该作者
    很有用        
    3偶尔光临
    208/300

    0

    主题

    91

    帖子

    208

    积分

    Rank: 3Rank: 3Rank: 3

    UID
    318682
    好友
    0
    蛮牛币
    338
    威望
    0
    注册时间
    2019-4-4
    在线时间
    117 小时
    最后登录
    2019-10-15
    9#
    2019-6-13 10:22:15 只看该作者
    感谢分享
    排名
    64937
    昨日变化

    0

    主题

    7

    帖子

    16

    积分

    Rank: 1

    UID
    180527
    好友
    1
    蛮牛币
    37
    威望
    0
    注册时间
    2016-11-16
    在线时间
    7 小时
    最后登录
    2019-7-2
    10#
    2019-6-13 10:49:00 只看该作者
    6666666666666666
    2初来乍到
    101/150
    排名
    13721
    昨日变化

    0

    主题

    8

    帖子

    101

    积分

    Rank: 2Rank: 2

    UID
    180322
    好友
    0
    蛮牛币
    304
    威望
    0
    注册时间
    2016-11-3
    在线时间
    31 小时
    最后登录
    2019-8-18
    11#
    2019-6-13 11:53:52 只看该作者
    感谢分享?。。?!
    5熟悉之中
    940/1000
    排名
    10707
    昨日变化

    0

    主题

    663

    帖子

    940

    积分

    Rank: 5Rank: 5

    UID
    301976
    好友
    1
    蛮牛币
    1408
    威望
    0
    注册时间
    2018-10-31
    在线时间
    179 小时
    最后登录
    2019-9-30
    12#
    2019-6-13 13:48:39 只看该作者
    感谢分享...
    排名
    64937
    昨日变化

    0

    主题

    11

    帖子

    22

    积分

    Rank: 1

    UID
    271058
    好友
    0
    蛮牛币
    24
    威望
    0
    注册时间
    2018-3-7
    在线时间
    9 小时
    最后登录
    2019-6-17
    13#
    2019-6-13 13:56:57 只看该作者
    366666666666666
    2初来乍到
    114/150
    排名
    13986
    昨日变化

    0

    主题

    16

    帖子

    114

    积分

    Rank: 2Rank: 2

    UID
    306015
    好友
    0
    蛮牛币
    106
    威望
    0
    注册时间
    2018-11-26
    在线时间
    38 小时
    最后登录
    2019-8-23
    14#
    2019-6-13 15:29:07 只看该作者
    感谢分享
    7日久生情
    1554/5000
    排名
    1338
    昨日变化

    8

    主题

    156

    帖子

    1554

    积分

    Rank: 7Rank: 7Rank: 7Rank: 7

    UID
    215462
    好友
    1
    蛮牛币
    3978
    威望
    0
    注册时间
    2017-3-30
    在线时间
    478 小时
    最后登录
    2019-10-19
    15#
    2019-6-13 17:12:23 只看该作者
    跟大佬学习操作
    您需要登录后才可以回帖 登录 | 注册帐号

    本版积分规则

  • 回复@大雨582:社会人不可能像自然人一样自由,你还得尊重别人的意愿!不然咱随便生产啥都强行让你要,咱是自由了,你呢? 2019-10-12
  • 发改委每月首个工作日公布限制乘坐火车飞机人员名单 2019-10-12
  • 全国啦啦操联赛临汾站开幕 2019-10-08
  • 《爱疯音乐家》陈洁仪:不完美的美其实更美爱疯音乐家陈洁仪 2019-10-08
  • 中冶置业兴隆百万平米新城正式亮相 将打造全配套高铁康养小镇 2019-10-06
  • 以政府拟严打“乱拍摄”以军行为 最高10年监禁 2019-10-03
  • 图解:关于中国梦,习近平总书记这十句话直抵人心 2019-09-20
  • “舌尖”之后, “风味”能否再续辉煌? 2019-09-17
  • 腾讯要花32亿收购《绝地求生》开发商10%股份 2019-09-17
  • 涨知识!文字里的秘密 最全甲骨文书展来了 2019-09-13
  • 晋中市着力将“厕所革命”进行到底 2019-09-05
  • 供应链如何更“智慧” 2019-09-05
  • 红枣食疗效果好 6种吃法最滋补-美食资讯 2019-09-01
  • 党的十九大最重大的理论成就 2019-08-28
  • 余世存:阅读经典不仅是消费,也是责任 2019-08-20
  • 188排球比分 蓝俎平码三中三论坛 彩票双色球走势图新浪爱彩 澳洲幸运10哪里有走势图 大乐透开奖 河南快三组合走势图 实况足球2019萨内蒂 宝马娱乐会所 宁夏11选5预测号码 浙江快乐彩手机版开奖走势图 辽宁35期35选七走势图 友博真人龙虎斗开户 重庆时时彩app安卓系统 足球指数怎么参考 快乐五行