iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >详解CocosCreator游戏之鱼群算法
  • 959
分享到

详解CocosCreator游戏之鱼群算法

2024-04-02 19:04:59 959人浏览 薄情痞子
摘要

前言 最近想学一下CocosCreator,于是,编辑器下载,启动。 众所周知,边写边学才是最快的学习方法,得写个Demo练练手,那么写什么呢?听说现在《墨虾探蝌》挺火的,那就抄(学

前言

最近想学一下CocosCreator,于是,编辑器下载,启动。

众所周知,边写边学才是最快的学习方法,得写个Demo练练手,那么写什么呢?听说现在《墨虾探蝌》挺火的,那就抄(学习的事怎么能叫抄呢?)写一个类似的小游戏吧!

(在《墨虾探蝌》中,鱼的位置固定,到达一定数量后玩家会升级,不会出现一大群鱼的情况,本项目其实和它不同,没有升级进化,是会有一大群鱼的,每条鱼也不是固定位置,而是有自己的运动逻辑,其实和另一个游戏更像,不过我不知道叫什么。。。)

效果展示:

正文

首先整一个玩家player:

图片资源用的是CocosCreator官方Demo的图片,照着官方Demo学习了一下,懒得找鱼的图片就直接把图片拿来用了,这个项目目前只用了两张图片

有了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:

墙上加上物理碰撞体:

然后就可以开始写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鱼群算法的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解CocosCreator游戏之鱼群算法

本文链接: https://www.lsjlt.com/news/123650.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解CocosCreator游戏之鱼群算法
    前言 最近想学一下CocosCreator,于是,编辑器下载,启动。 众所周知,边写边学才是最快的学习方法,得写个Demo练练手,那么写什么呢?听说现在《墨虾探蝌》挺火的,那就抄(学...
    99+
    2024-04-02
  • 详解CocosCreator制作射击游戏
    目录场景布置游戏资源炮塔旋转动态生成子弹碰撞计算增加效果靶子移动增加弹药库的显示Common公共类ResultDialog脚本(控制分数提示框)Common脚本游戏重开更改Commo...
    99+
    2024-04-02
  • Python 蚁群算法详解
    目录蚁群算法简介TSP问题描述蚁群算法原理代码实现总结 蚁群算法简介 蚁群算法(Ant Clony Optimization, ACO)是一种群智能算法,它是由一群无智能或有轻微智能...
    99+
    2024-04-02
  • JS/HTML5游戏常用算法之追踪算法的示例分析
    这篇文章主要为大家展示了“JS/HTML5游戏常用算法之追踪算法的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JS/HTML5游戏常用算法之追踪算法的...
    99+
    2024-04-02
  • Vue3diff算法之双端diff算法详解
    目录双端Diff算法双端比较的原理简单Diff的不足双端Diff介绍Diff流程第一次diff第二次diff第三次diff第四次diff双端Diff的优势非理想情况的处理方式添加新元...
    99+
    2024-04-02
  • C++游戏教程基本技巧之随机化详解
    目录0. 引言1. 随机数 rand()2. 设置随机种子 srand()3. 时间 time()4. 随机排列 random_shuffle()5. 随机基本案例5-1. 随机 0...
    99+
    2022-11-13
    C++随机数 C++随机
  • 通过CartPole游戏详解PPO优化的方法
    本篇内容主要讲解“通过CartPole游戏详解PPO优化的方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“通过CartPole游戏详解PPO优化的方法”吧!CartPole 介绍在一个光滑的轨...
    99+
    2023-07-06
  • Qt实现打地鼠游戏的方法详解
    目录功能讲解1.确定地鼠数量2.定义游戏难易程序3.难度切换4.定时器处理总结今天与大家分享一个小游戏的实现:打地鼠 看一下实现效果吧~ 在指定的时间内打中一定数额的地鼠,否则失败...
    99+
    2022-11-13
    Qt打地鼠游戏 Qt 打地鼠 Qt 游戏
  • C语言实现扫雷小游戏完整算法详解(附完整代码)
    目录前言1.算法基本思路2.算法详解1.初始化数组与打印数组2.设置雷3.排查与标记4.CountMine函数计算周围雷的个数 5.ExpandMine函数递归展开周围所有...
    99+
    2024-04-02
  • Java实现简易拼图游戏的方法详解
    目录效果展示游戏结构实现代码效果展示 介绍:游戏共有五张图片可以选择,分成了4 X 4 十六个方格,点击开始就可以开始游戏。游戏运行的截图如下: 游戏结构 实现代码 代码如下:M...
    99+
    2024-04-02
  • Python代码实现粒子群算法图文详解
    目录1.引言2.算法的具体描述:2.1原理2.2标准粒子群算法流程3.代码案例3.1问题3.2绘图 3.3计算适应度3.4更新速度3.5更新粒子位置3.6主要算法过程结果总...
    99+
    2024-04-02
  • Python 经典贪心算法之Prim算法案例详解
    最小生成树的Prim算法也是贪心算法的一大经典应用。Prim算法的特点是时刻维护一棵树,算法不断加边,加的过程始终是一棵树。 Prim算法过程: 一条边一条边地加, 维护一棵树。 初...
    99+
    2024-04-02
  • Python数据结构与算法之算法分析详解
    目录0. 学习目标1. 算法的设计要求1.1 算法评价的标准1.2 算法选择的原则2. 算法效率分析2.1 大O表示法2.2 常见算法复杂度2.3 复杂度对比3. 算法的存储空间需求...
    99+
    2024-04-02
  • JS/HTML5游戏常用算法之碰撞检测和像素检测算法的示例分析
    这篇文章主要介绍JS/HTML5游戏常用算法之碰撞检测和像素检测算法的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体如下:使用像素碰撞检测法算是最精确的算法了,当然,带来...
    99+
    2024-04-02
  • C语言编程之扫雷小游戏空白展开算法优化
    目录写代码前,扫雷需要什么进行主函数文件的代码game文件以及函数步骤在主函数文件中使用game函数布值棋盘(雷盘和玩家棋盘)打印棋盘函数玩家排雷计算雷数的函数空白递归算法写代码前,...
    99+
    2024-04-02
  • GoJava算法之累加数示例详解
    目录累加数方法一:穷举法(java)方法二:深度优先遍历(go)累加数 累加数 是一个字符串,组成它的数字可以形成累加序列。 一个有效的 累加序列 必须 至少 包含 3 个数。除了最...
    99+
    2024-04-02
  • 使用actor-critic方法来控制CartPole-V0游戏详解
    目录CartPole 介绍Actor Critic 介绍库准备设置超参数Actor Critic 结构搭建训练前的样子模型训练训练后的样子CartPole 介绍 在一个光滑的轨道上...
    99+
    2023-05-15
    actor-critic控制CartPole-V0 CartPole 游戏
  • GoJava算法之找不同示例详解
    目录找不同方法一:计数(Java)方法二:求和(Go)找不同 给定两个字符串 s 和 t ,它们只包含小写字母。 字符串 t 由字符串 s 随机重排,然后在随机位...
    99+
    2024-04-02
  • 计算机中win10系统无法进入LOL游戏的解决方法
    这篇文章主要介绍了计算机中win10系统无法进入LOL游戏的解决方法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。win10系统无法登陆lol英雄联盟游戏怎么办?win10系...
    99+
    2023-06-10
  • Python干货实战之八音符酱小游戏全过程详解
    目录导语🎼正文🎼🎵一、准备中1.1 游戏规则🎶1.2 素材图片🎶🎵二、环境2.1 小介...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作