国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - js教程 - 詳解CocosCreator游戲之魚群算法

詳解CocosCreator游戲之魚群算法

2022-03-02 16:39immortaler js教程

這篇文章主要介紹了詳解CocosCreator游戲之魚群算法,對算法感興趣的同學,可以仔細參考下

前言

最近想學一下CocosCreator,于是,編輯器下載,啟動。

眾所周知,邊寫邊學才是最快的學習方法,得寫個Demo練練手,那么寫什么呢?聽說現在《墨蝦探蝌》挺火的,那就抄(學習的事怎么能叫抄呢?)寫一個類似的小游戲吧!

(在《墨蝦探蝌》中,魚的位置固定,到達一定數量后玩家會升級,不會出現一大群魚的情況,本項目其實和它不同,沒有升級進化,是會有一大群魚的,每條魚也不是固定位置,而是有自己的運動邏輯,其實和另一個游戲更像,不過我不知道叫什么。。。)

效果展示:

詳解CocosCreator游戲之魚群算法

正文

首先整一個玩家player:

詳解CocosCreator游戲之魚群算法

圖片資源用的是CocosCreator官方Demo的圖片,照著官方Demo學習了一下,懶得找魚的圖片就直接把圖片拿來用了,這個項目目前只用了兩張圖片

詳解CocosCreator游戲之魚群算法

有了player就得寫個player控制腳本,點擊一個方向,player就會一直向這個方向移動。那么我們首先需要獲取玩家點擊的位置,然后計算出player移動的方向,我們把這個寫在GameManager里面,所以新建一個腳本GameManager,這個腳本掛在Canvas上。

先定義兩個變量,玩家節點和方向向量:

@property(cc.Node)
player: cc.Node = null;
ir: cc.Vec2 = cc.Vec2.ZERO;

獲取方向的方法:

getClickDir(event) {
    let pos: cc.Vec2 = event.getLocation();
    //轉本地坐標
    let localPos = this.node.convertToNodeSpaceAR(pos);
    let playerPos: cc.Vec2 = new cc.Vec2(
        this.player.position.x,
        this.player.position.y
    );

    let len = localPos.sub(playerPos).mag();

    this.dir.x = localPos.sub(playerPos).x / len;
    this.dir.y = localPos.sub(playerPos).y / len;
}

這方法在onMouseDown和onMouseMove時調用:

onMouseDown(event) {
    if (event.getButton() == cc.Event.EventMouse.BUTTON_LEFT) {
        this.getClickDir(event);
    }
}

onMouseMove(event) {
    if (event.getButton() == cc.Event.EventMouse.BUTTON_LEFT) {
        this.getClickDir(event);
    }
}

onLoad() {
    cc.director.getCollisionManager().enabled = true;
    cc.director.getPhysicsManager().enabled = true;

    this.node.on(cc.Node.EventType.MOUSE_DOWN, this.onMouseDown, this);
    this.node.on(cc.Node.EventType.MOUSE_MOVE, this.onMouseMove, this);
}

onDestroy() {
    this.node.off(cc.Node.EventType.MOUSE_DOWN, this.onMouseDown, this);
    this.node.off(cc.Node.EventType.MOUSE_MOVE, this.onMouseMove, this);
}

有了方向向量,就可以讓玩家移動了,新建一個FishPlayer腳本。

為了不讓玩家亂跑,我們先 build the wall:

詳解CocosCreator游戲之魚群算法

墻上加上物理碰撞體:

詳解CocosCreator游戲之魚群算法

然后就可以開始寫FishPlayer腳本了,先把要用到的變量都定義一下:

@property(cc.Node)
camera: cc.Node = null;

@property(cc.Node)
gameManager: cc.Node = null;

game: GameManager;
speed: number = 170;
velocity: cc.Vec3 = cc.Vec3.ZERO;

在onLoad()中給game賦值:

onLoad() {
    this.game = this.gameManager.getComponent("GameManager");
}

通過射線來檢測邊界,判斷玩家是否能移動的方法:

canMove() {
    var flag: boolean = true;
    //前方有障礙物
    var pos = this.node.convertToWorldSpaceAR(cc.Vec3.ZERO);
    var endPos = pos.add(this.node.up.mul(40));
    var hit: cc.PhysicsRayCastResult[] = cc.director
        .getPhysicsManager()
        .rayCast(
            new cc.Vec2(pos.x, pos.y),
            new cc.Vec2(endPos.x, endPos.y),
            cc.RayCastType.All
        );
    if (hit.length > 0) {
        flag = false;
    }
    return flag;
}

在update中控制玩家移動:

update(dt) {
    if (this.game.dir.mag() < 0.5) {
        this.velocity = cc.Vec3.ZERO;
        return;
    }

    let vx: number = this.game.dir.x * this.speed;
    let vy: number = this.game.dir.y * this.speed;

    this.velocity = new cc.Vec3(vx, vy);
    //移動
    if (this.canMove()) {
        this.node.x += vx * dt;
        this.node.y += vy * dt;
    }

    //相機跟隨
    this.camera.setPosition(this.node.position);

    //向運動方向旋轉
    let hudu = Math.atan2(this.game.dir.y, this.game.dir.x);
    let angle = hudu * (180 / Math.PI);
    angle = 360 - angle + 90;
    this.node.angle = -angle;
}

玩家的移動邏輯寫完了,接下來寫魚群。

新建一個FishGroupManager腳本和一個FishGroup腳本,FishGroupManager掛在Canvas上,FishGroup掛在player上。

FishGroupManager中定義一個靜態fishGroups變量,用來管理所有Group(因為場景中可能有多個玩家,多個魚群,現在只有一個玩家,這里方便之后擴展):

static fishGroups: FishGroup[]; //所有組

來一個把group加入groups的靜態方法:

static AddGroup(group: FishGroup) {
    if (this.fishGroups == null) this.fishGroups = new Array();

    if (this.fishGroups.indexOf(group) == -1) this.fishGroups.push(group);
}

再來一個獲取group的靜態方法(根據索引獲取):

static GetFishGroup(index: number) {
    for (var i = 0; i < this.fishGroups.length; i++)
        if (this.fishGroups[i].groupID == index) return this.fishGroups[i];
}

FishGroupManager就寫完了,接下來再寫FishGroup,把上面用到的groupID定義一下,還有魚群數組:

groupID: number = 0; //組id    
fishArr: cc.Component[] = new Array<cc.Component>();

在onLoad中將自身加到fishGroups里面:

onLoad() {
    FishGroupManager.AddGroup(this);
}

現在魚群有了,但是里面一條魚都沒有,所以我們還要一個抓魚的方法:

catchFish(fish) {
    this.fishArr.push(fish);
}

再定義一些要用到的參數,FishGroup就寫完了:

keepMinDistance: number = 80;
keepMaxDistance: number = 100;
keepWeight: number = 1; //成員保持距離和保持距離權重
moveWeight: number = 0.8; //和成員移動權重

接下來就到了重頭戲了――魚群中其他小魚的運動邏輯。

直接將player復制一下,把掛載的FishPlayer和FishGroup腳本去掉,命名為fish,這就是我們的小魚了,把它做成預制。然后新建一個FishBehaviour腳本,這個腳本掛在player和普通小魚身上。

首先實現“抓魚”功能,當player靠近小魚后,小魚就被捕獲,成為該player魚群中的一員。

定義相關變量:

@property(cc.Node)
gameManager: cc.Node = null;
game: GameManager;
isPicked: boolean = false;
pickRadius: number = 50; //抓取距離

groupId: number = -1; //組 id
myGroup: FishGroup;

同樣,在onLoad()中給game賦值:

onLoad() {
    this.game = this.gameManager.getComponent(GameManager);
}

判斷和player距離的方法:

getPlayerDistance() {
    let dist = this.node.position.sub(this.game.player.position).mag();
    return dist;
}

加入魚群方法:

onPicked() {
    //設置group
    this.groupId = this.game.player.getComponent(FishGroup).groupID;
    this.myGroup = FishGroupManager.GetFishGroup(this.groupId);

    if (this.myGroup != null) {
        this.myGroup.catchFish(this);
        this.isPicked = true;
    }
}

在update中調用:

update(dt) {
    if (this.isPicked) {
        //隨著魚群移動
    }
    else {
        if (this.getPlayerDistance() < this.pickRadius) {
            this.onPicked();
        }
    }
}

OK,現在小魚到魚群中了,怎么隨著魚群一起移動呢?

這里主要有兩個點:

1.小魚會隨著周圍“鄰居魚”一起移動

2.小魚之間要保持距離,不能太過擁擠

所以我們需要計算小魚周圍一定范圍內魚群運動向量的平均值,這樣還不夠,還要判斷是否“擁擠”,“擁擠”的話就增加一個遠離的趨勢,太遠的話就增加一個靠近的趨勢,再分別乘以權重,加起來,就可以得到我們要的向量了,代碼如下:

定義變量:

moveSpeed: number = 170;
rotateSpeed: number = 40; //移動旋轉速度
neighborRadius: number = 500; //距離小于500算是鄰居

speed: number = 0;
currentSpeed: number = 0;
myMovement: cc.Vec3 = cc.Vec3.ZERO;

求平均向量:

GetGroupMovement() {
        var v1: cc.Vec3 = cc.Vec3.ZERO;
        var v2: cc.Vec3 = cc.Vec3.ZERO;
 
        for (var i = 0; i < this.myGroup.fishArr.length; i++) {
            var otherFish: FishBehaviour = this.myGroup.fishArr[i].getComponent(
                FishBehaviour
            );
 
            var dis = this.node.position.sub(otherFish.node.position); //距離
 
            //不是鄰居
            if (dis.mag() > this.neighborRadius) {
                continue;
            }
 
            var v: cc.Vec3 = cc.Vec3.ZERO;
            //大于最大間隔,靠近
            if (dis.mag() > this.myGroup.keepMaxDistance) {
                v = dis.normalize().mul(1 - dis.mag() / this.myGroup.keepMaxDistance);
            }
            //小于最小間隔,遠離
            else if (dis.mag() < this.myGroup.keepMinDistance) {
                v = dis.normalize().mul(1 - dis.mag() / this.myGroup.keepMinDistance);
            } else {
                continue;
            }
 
            v1 = v1.add(v); //與周圍單位的距離
            v2 = v2.add(otherFish.myMovement); //周圍單位移動方向
        }
 
        //添加權重因素
        v1 = v1.normalize().mul(this.myGroup.keepWeight);
        v2 = v2.normalize().mul(this.myGroup.moveWeight);
        var ret = v1.add(v2);
        return ret;
    }

現在,可以把update補全了:

update(dt) {
        //隨著魚群移動
        if (this.isPicked) {
            var direction = cc.Vec3.ZERO;
            if (this.node.name != "player") {
                direction = direction.add(this.GetGroupMovement());
            }
 
            this.speed = cc.misc.lerp(this.speed, this.moveSpeed, 2 * dt);
            this.Drive(direction, this.speed, dt); //移動
        }
        //捕獲
        else {
            if (this.getPlayerDistance() < this.pickRadius) {
                this.onPicked();
            }
        }
    }

Drive()方法:

Drive(direction: cc.Vec3, spd: number, dt) {
    var finialDirection: cc.Vec3 = direction.normalize();
    var finialSpeed: number = spd;
    var finialRotate: number = 0;
    var rotateDir: number = cc.Vec3.dot(finialDirection, this.node.right);
    var forwardDir: number = cc.Vec3.dot(finialDirection, this.node.up);

    if (forwardDir < 0) {
        rotateDir = Math.sign(rotateDir);
    }

    //防抖
    if (forwardDir < 0.98) {
        finialRotate = cc.misc.clampf(
            rotateDir * 180,
            -this.rotateSpeed,
            this.rotateSpeed
        );
    }

    finialSpeed *= cc.misc.clamp01(direction.mag());
    finialSpeed *= cc.misc.clamp01(1 - Math.abs(rotateDir) * 0.8);
    if (Math.abs(finialSpeed) < 0.01) {
        finialSpeed = 0;
    }

    //移動
    if (this.canMove()) {
        this.node.x += this.node.up.x * finialSpeed * dt;
        this.node.y += this.node.up.y * finialSpeed * dt;
    }

    //旋轉
    var angle1 = finialRotate * 8 * dt;
    var angle2 = this.node.angle - angle1;
    this.node.angle = angle2 % 360;

    this.currentSpeed = finialSpeed;
    this.myMovement = direction.mul(finialSpeed);
}

canMove() {
    var flag: boolean = true;
    //前方有障礙物
    var pos = this.node.convertToWorldSpaceAR(cc.Vec3.ZERO);
    var endPos = pos.add(this.node.up.mul(40));
    var hit: cc.PhysicsRayCastResult[] = cc.director
        .getPhysicsManager()
        .rayCast(
            new cc.Vec2(pos.x, pos.y),
            new cc.Vec2(endPos.x, endPos.y),
            cc.RayCastType.All
        );
    if (hit.length > 0) {
        flag = false;
    }
    return flag;
}

以上就是詳解CocosCreator游戲之魚群算法的詳細內容,更多關于CocosCreator魚群算法的資料請關注服務器之家其它相關文章!

原文鏈接:https://blog.csdn.net/a245017940/article/details/106499050

延伸 · 閱讀

精彩推薦
  • js教程JavaScript實現鼠標經過表格行給出顏色標識

    JavaScript實現鼠標經過表格行給出顏色標識

    這篇文章主要為大家詳細介紹了JavaScript實現鼠標經過表格行給出顏色標識,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參...

    KathyLJQ8562022-02-27
  • js教程詳解ES6實現類的私有變量的幾種寫法

    詳解ES6實現類的私有變量的幾種寫法

    這篇文章主要介紹了詳解ES6實現類的私有變量的幾種寫法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    Do_Better11932022-01-20
  • js教程JavaScript實現前端網頁版倒計時

    JavaScript實現前端網頁版倒計時

    這篇文章主要為大家詳細介紹了JavaScript實現前端網頁版倒計時,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    小 菜5892022-02-20
  • js教程利用javaScript處理常用事件詳解

    利用javaScript處理常用事件詳解

    這篇文章主要介紹了利用javaScript處理常用事件詳解,文章有非常詳細的代碼實踐,對學習js的小伙伴們有一定的參考價值,需要的朋友可以參考下...

    花狗Fdog3552022-03-01
  • js教程CocosCreator經典入門項目之flappybird

    CocosCreator經典入門項目之flappybird

    這篇文章主要介紹了CocosCreator經典入門項目之flappybird,詳細說明了制作的每個步驟,還有詳盡的代碼,對CocosCreator感興趣的同學,一定要看一下...

    冏尼9662022-03-01
  • js教程原生js拖拽功能制作滑動條實例代碼

    原生js拖拽功能制作滑動條實例代碼

    這篇文章主要介紹了原生js拖拽功能制作滑動條實例教程,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的...

    蔣偉平3522022-01-17
  • js教程JS數組去重的九種高階方法(親測有效)

    JS數組去重的九種高階方法(親測有效)

    這篇文章主要給大家介紹了關于JS數組去重的九種高階方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的...

    Echoyya、6872022-02-23
  • js教程JavaScript WeakMap使用詳解

    JavaScript WeakMap使用詳解

    這篇文章主要介紹了JavaScript WeakMap使用的詳細介紹,幫助大家更好的理解和使用JavaScript,感興趣的朋友可以了解下...

    MDN7132022-01-17
主站蜘蛛池模板: 亚洲国产精品久久久久久久久久久 | 久久久久国产精品 | a级三四级黄大片 | 99国产一区 | 午夜影院网站 | www.99re| 成人av电影在线 | 精品日韩一区 | 91色在线观看 | 中国黄色一级 | 黄色w网站 | 二区影院 | 在线不卡一区 | 91国自产精品中文字幕亚洲 | 久久国产成人 | 色婷婷在线视频观看 | 亚洲国产精品激情在线观看 | 中文字幕在线观看视频一区 | 在线中文字幕视频 | 91av在线视频观看 | 成人在线免费视频 | 一区影院 | 一本一道久久精品综合 | 黄色在线免费观看视频网站 | 亚洲三级在线观看 | 狠狠操影院| 欧美成人免费网站 | 亚洲精品一区二区三区在线 | 亚洲欧美日韩精品久久亚洲区 | 一区二区在线视频 | 久久久久国产精品 | 狠狠艹| 欧美午夜一区 | 精品久久久久久久久久久久久久 | 天天澡天天狠天天天做 | 五月激情综合网 | 欧美视频精品 | 色综合色综合网色综合 | 伊人精品影院 | 影音先锋国产精品 | 欧美综合一区二区三区 |