物体移动控制

要求

1、物体移动主要因素:速度、方向;
2、移动的范围不能超出屏幕边界,当碰触到屏幕左右边界时,应更换方向;
3、可开启追踪模式,如果与Player在一定距离内,则追踪Player,否则,正常运行;
4、应增加一定随机性,增加运动的趣味性;

知识点

环境:
2D游戏;
物体通过Rigidbody2d来控制;
Rigidbody2d设置BodyType属性为Kinematic(不受重力等影响,可通过velocity来控制其运动)

1、Rigidbody2d.velocity分析

1、velocity可表示物体的速度和方向性
2、数值越大,表示速度越快
3、坐标系上、右为正方向,数值应为正数
4、坐标系左、下为负方向,数值应为负数

2、运动AI分析

1、碰撞屏幕边界自动转向能力:y方向不变,数值可波动;x方向改变,数值可波动;
2、自动追踪能力:距离限定
3、波动性:随机系数

3、追踪分析

1、一个正常运动的物体如何朝向另外一个物体运动,且速率基本不变?
2、计算两物体向量差值,取其normalized,指定给rigidbody.velocity属性即可实现追踪能力

Vector2 vDistance = target.transform.position - transform.position;
rigidbody.velocity = vDistance.normalized;

代码实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyMove : MonoBehaviour {
    //velocity可表示物体的速度和方向性
    //数值越大,表示速度越快
    //坐标系上、右为正方向,数值应为正数
    //坐标系左、下为负方向,数值应为负数

    //运动AI:
    //1、碰撞屏幕边界自动转向能力:y方向不变,数值可波动;x方向改变,数值可波动;
    //2、自动追踪能力:距离限定
    //3、波动性:随机系数

    public float velocity_x = 0f;//x方向速度
    public float velocity_y = 0f;//y方向速度
    public float fluctuate = 0f;//速度波动范围

    public bool doFollow = false;//追踪?
    public float doFollowDistance = 2f;//触发追踪的有效距离

    private bool isFollowing = false;//是否正在追踪
    private Rigidbody2D rigidbody;//2D刚体组件

    /// <summary>
    /// 初始化运动参数
    /// </summary>
    /// <param name="velocity_x">x方向速度</param>
    /// <param name="velocity_y">y方向速度</param>
    /// <param name="fluctuate">速度波动范围</param>
    public void reInit(float velocity_x, float velocity_y, float fluctuate){
        this.velocity_x = velocity_x;
        this.velocity_y = velocity_y;
        this.fluctuate = fluctuate;
    }

    /// <summary>
    /// 设置追踪参数
    /// </summary>
    /// <param name="doFollow">是否开启追踪模式</param>
    /// <param name="doFollowDistance">触发追踪的有效距离</param>
    public void setDoFollow(bool doFollow, float doFollowDistance){
        this.doFollow = doFollow;
        this.doFollowDistance = doFollowDistance;
    }

    void Awake() {
        rigidbody = GetComponent<Rigidbody2D> ();
    }


    void Update () {
        if(rigidbody == null){
            return;
        }

        //如果开启了追踪模式,则执行自动追踪能力AI
        if(doFollow){
            moveFollow ();
        }

        //碰撞屏幕边界自动转向能力,同时波动速度大小
        moveRotation ();
        //移动
        move ();
    }

    /// <summary>
    /// 移动
    /// </summary>
    void move(){
        //如果没有在追踪,则按照正常速度运行
        if(!isFollowing){
            rigidbody.velocity = new Vector2 (velocity_x, velocity_y);
        }
    }

    /// <summary>
    /// 碰撞屏幕边界自动转向能力,同时波动速度大小
    /// </summary>
    private void moveRotation(){
        //检测是否与屏幕边界碰撞,如果是,则改变方向
        if (AppCommon.isWidthBoundary (transform, velocity_x)) {
            //仅x方向改变即可
            velocity_x = -velocity_x;
            //根据波动范围,波动速度的数值,使其效果更随机,AI更灵活智能
            velocity_x = doFluctuate (velocity_x);
            velocity_y = doFluctuate (velocity_y);
        }
    }

    /// <summary>
    /// 追踪模式
    /// </summary>
    private void moveFollow(){
        //如果设定的距离为0,则不追踪
        if(doFollowDistance == 0){
            return;
        }
        //获得追踪目标对象,即Player
        GameObject target = GameObject.FindGameObjectWithTag ("Player");
        if(target == null){
            return;
        }
        //获得与目标对象的距离,如果在触发追踪的有效距离内,则开始追踪Player
        float distance = Vector2.Distance (target.transform.position, transform.position);
        if(distance > doFollowDistance){
            isFollowing = false;
            return;
        }
        isFollowing = true;
        //计算两物体向量差值,取其normalized,指定给rigidbody.velocity属性即可实现追踪能力
        Vector2 vDistance = target.transform.position - transform.position;
        rigidbody.velocity = vDistance.normalized;
    }

    /// <summary>
    /// 波动数据
    /// </summary>
    /// <returns>波动后的值</returns>
    /// <param name="value">需要波动的值</param>
    private float doFluctuate(float value){
        if (fluctuate != 0) {
            float fluctuateAbs = Mathf.Abs (fluctuate);
            float fluctuateRandom =  Random.Range (-fluctuateAbs, fluctuateAbs);
            value += fluctuateRandom;
        }
        return value;
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AppCommon : MonoBehaviour {

    public static float app_velocity = 1f;

    private static float width;
    private static float height;
    private static float left;
    private static float right;
    private static float top;
    private static float bottom;

    void Start ()
    {
        Camera cam = Camera.main;
        height = 2f * cam.orthographicSize;
        width = height * cam.aspect;

        left = -width / 2;
        right = width / 2;
        top = height / 2;
        bottom = -height / 2;
    }

    /// <summary>
    /// Ises the width boundary.
    /// </summary>
    /// <returns><c>true</c>, if width boundary was ised, <c>false</c> otherwise.</returns>
    /// <param name="transform">Transform.</param>
    /// <param name="h">The height.</param>
    public static bool isWidthBoundary (Transform transform, float h)
    {
        //在left与right之间,返回false
        //在left左边,手势为正方向,返回false
        //在right右边,手势为反方向,返回false
        if (transform.position.x > left && transform.position.x < right) {
            return false;
        }
        if (transform.position.x < left && h > 0) {
            return false;
        }
        if (transform.position.x > right && h < 0) {
            return false;
        }
        return true;
    }

    /// <summary>
    /// Ises the height boundary.
    /// </summary>
    /// <returns><c>true</c>, if height boundary was ised, <c>false</c> otherwise.</returns>
    /// <param name="transform">Transform.</param>
    /// <param name="v">V.</param>
    public static bool isHeightBoundary (Transform transform,float v)
    {
        //在top与right之间,返回false
        //在top上边,手势为正方向,返回false
        //在bottom下边,手势为反方向,返回false
        if (transform.position.y > bottom && transform.position.y < top) {
            return false;
        }
        if (transform.position.y > top && v < 0) {
            return false;
        }
        if (transform.position.y < bottom && v > 0) {
            return false;
        }
        return true;
    }
}

效果

3.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容