《 ⭐ Cocos 论坛首发 Bookmarklet 简略编写 + Snip 片段 + 自定义查看动态合图 + 简要自定义 WebGL 图形(有写插件) + RVO避障简析 (附写源码) | 社区征文》

:elephant: Cocos 论坛首发 Bookmarklet 简略编写 + Snip 片段 + 自定义查看动态合图 + 简要自定义 WebGL 图形(有写插件)+ RVO避障简析 (附写源码) | 社区征文》

申明 [本文使用的技术很菜_仅供不太了解这块的朋友稍作阅读] :rofl:

本文章使用的技术并不复杂, 想做深入技术研究的大佬可以略过了

文内并无比较高深的技术内容, 最后感谢各位大佬的阅读

本文主旨:言简意赅 · 词可达意 :star: 言之有理 · 言而有物

本文内容目录概览
使用方法和效果:::
1.8.1 :wolf:使用 Bookmarklet + Snip 片段的 GIF 操作演示
1.8.2 演示=>自定义查看动态合图 (拓展 Cocos 2.x 的一个查看的功能) 及效果
正文:::
1. :elephant: Cocos 论坛首发 Bookmarklet 简略编写 + Snip 片段介绍
1.1 在任何网页上运行 JavaScript 的代码片段
1.2 「引用」本条目参考自 Bookmarklet编写指南 - 阮一峰日志
1.3 什么是 Bookmarklet ?
1.4 Bookmarklet 的优点
1.5 Bookmarklet 的编写规则
1.6 Bookmarklet 的编写技巧
1.7 Snip 片段简介 (这里指的是浏览器控制台里面的 Snip 片段代码)
1.9 使用 Bookmarklet + Snip 片段的 GIF 操作演示代码
2. :dog2: 在 Cocos 内用代码写最简单的 WebGL图形(Ts) + 超小杯 [付费插件] :cat2:
3. RVO 避障的简单理解 + [免费源码] :star:

先看使用方法和效果:=>

1.8.1 :wolf:使用 Bookmarklet + Snip 片段的 GIF 操作演示

1.8.2 演示=>自定义查看动态合图 (拓展 Cocos 2.x 的一个查看的功能) 及效果

1.8.3 在浏览器上使用 Bookmarklet 片段代码(先复制好要用的 Js 代码)

收藏书签, 书签的网址栏, 填入 Bookmarklet 片段代码, 点击书签即可使用

1.8.4 在浏览器上使用 Snip 片段代码(先复制好要用的 Js 代码)

ctrl+shift+p 输入 snip 选择新建 snip, 填入 Snip 片段代码, ctrl+enter 快捷键运行代码(可换行)

正文介绍:=>

1. :elephant: Cocos 论坛首发 Bookmarklet 简略编写 + Snip 片段介绍

1.1 「引子」 看了下论坛内容, 并没有发现有讲述这个内容的大佬, 所以小弟来抛砖引玉 :lion:
1.1.1「述1」本文内的小图标采用用的是 Unicode 特殊编码格式表
1.1.2「述2」本文内录制的 GIF 图片,里面使用录屏软件, 参考之前写的这个介绍
1.2.1 「引用」本条目参考 Bookmarklet 编写指南 - 阮一峰日志
1.2.2 「引用」本条目参考 中文网最全 Bookmarklet 小书签 - 奔跑中的奶酪
1.2.3 「注」因为时间久远, 浏览器逐渐废除了很多相关代码, 所以使用老代码时需要考虑目前的执行环境(本文内的代码目前还能使用)

1.3 什么是 Bookmarklet ?

  • 内容介绍
    Bookmarklet ,由 Bookmark(书签)和-let(小的)构成,中文可以译成"书签工具"。它在形式上与"书签"一样,都保存在浏览器收藏夹里, 是一段Javascript代码,以"javascript:"开头。点击之后,会对当前页面执行某种操作。
    Bookmarklet,由英文单词 BookmarkApplet 组合而来,也就是书签小程序的意思。简单地说,小书签就是把一段带有特定功能的 JavaScript 代码保存到收藏夹,当你需要的时候,点击它可以实现这段 JavaScript 代码的功能。
  • 功能介绍
    小书签无需安装,也就不会对浏览器速度造成任何的影响,它和普通的书签一样放到书签栏里,只需要点击一下就能实现各式各样的功能,比如生成二维码,生成短链接,阅读模式等等功能,十分方便

1.4 Bookmarklet 的优点

1.4.1 安装快速

Bookmarklet的安装,就是在收藏夹中保存一段代码,一步就能完成。所有浏览器都原生支持。

1.4.2 使用方便

用的时候,点一下这个链接就行了。

1.4.3 开发容易

一段Javascript代码就是Bookmarklet的所有内容,不需要用到其他技术,比开发一个浏览器插件简单多了。

1.4.4 跨浏览器 (PS:在IOS系统里面的浏览器上也可以当做书签点击运行)

所有浏览器都支持Bookmarklet。如果写的正确,同样一个Bookmarklet在各种浏览器上都能正常使用。

1.5 Bookmarklet 的编写规则

1.5.1 必须以"javascript:"开头

浏览器把"javascript:"当做协议看待。有了它,
浏览器才知道要用javascript解释后面的代码。它的作用等同于将代码放在之间运行。

最简单的代码::
javascript:alert(‘Bookmarklets Cocos !’);

1.5.2 所有代码必须在同一行 (Snip 片段比这好,而且长度没限制)

因为浏览器把Bookmarklet当做网址保存,而网址是不能分行的,所以Bookmarklet也不能分行。
另一方面,网址是有长度限制的。IE的最长网址不能超过2083个字符(IE6不能超过508个字符),
这也就是Bookmarklet的最长长度。压缩工具 可以帮忙减少长度,
但是连接外部代码,可以避开这个限制。

1.5.3 使用单引号

根据Javascript的语法,单引号(‘xxx’)和双引号(“xxx”)都能使用。
但是由于html语言主要使用双引号,所以Bookmarklet优先使用单引号。
万一遇到必须使用双引号的情况,就采用它的URL编码形式"%22"。

1.5.4 不要污染全局变量

Bookmarklet最好不要生成新的全局变量,可以采用直接运行匿名函数的方式:

javascript: (function(){…})();

上面式子的第一个括号,定义了一个匿名函数;最后一个括号表示立即执行这个匿名函数。
所有的变量都是匿名函数的内部变量,不会生成任何新的全局变量。
如果必须设置全局变量,就取罕见的变量名(比如 CCoRCEoAsTOR2022 ),防止与已经存在的全局变量同名。

1.5.5 对文本和URL进行编码

为了防止出现非法字符,代码以外的文本都应该使用encodeURIComponent()函数进行编码,比如把空格变成%20。

1.6 Bookmarklet的编写技巧

1.6.1 获取网页信息 (可用)

1.6.1.2 获取当前网页信息-复制下方代码, 粘贴到浏览器控制台, 回车看效果

javascript:(()=>{var t;t = (function () {if (window.getSelection) {return window.getSelection().toString();} else if (document.getSelection) {return document.getSelection();} else if (document.selection) {return document.selection.createRange().text;};return '';})();alert('当前页面的标题:\n'+document.title+'\n当前页面的URL:\n'+location.href+'\n当前选中的文本\n\n'+t);})()

获取当前页面的标题:document.title
获取当前页面的URL: location.href
获取当前选中的文本:

var t;
t = (function () {
    if (window.getSelection) {
        return window.getSelection().toString();
    } else if (document.getSelection) {
        return document.getSelection();
    } else if (document.selection) {
        return document.selection.createRange().text;
    };
    return '';
})();
alert('当前页面的标题:\n'+document.title+'\n当前页面的URL:\n'+location.href+'\n当前选中的文本\n\n'+t);

1.6.2 防止刷新页面 (已失效)

如果代码对页面有改动(比如使用了document.write),浏览器就会用一个新页面替换原有页面。
所以最好用void()命令,把语句放在里面。
举例来说,下面这个 Bookmarklet 会导致原页面被一个新页面替代:

javascript:document.links[0].href=‘https://forum.cocos.org/’;

// 加上void以后,页面就不会跳转了:

javascript:void(document.links[0].href=‘https://forum.cocos.org/’);

1.7 Snip 片段简介 (这里指的是浏览器控制台里面的 Snip 片段代码)

  • Code snippet (代码片段)在VS中指的是 基于IDE支持的利用快捷方式快速输入一小段 ,或者称之为一整块代码的功能,在日常编程,特别是在工作中写内容相似的业务代码时,利用Snippet功能,可以极大加快编程效率

1.9 使用 Bookmarklet + Snip 片段的 GIF 操作演示代码 (条目 1.8.1 的使用演示效果在最上面)

「注」代码前演示的是如何使用 Bookmarklet 和 Snip 片段以及其效果, (个人建议用 Snip 片段)

1.9.1 改写记录:::=>

  • 改动来源:: 动态合图·调试文档 动态合图 · Cocos Creator · 2.x 文档
  • 如果希望看到动态合图的效果,那么可以开启调试来看到最终生成的大图,这些大图会添加到一个 ScrollView 展示出来。
// 开启调试
cc.dynamicAtlasManager.showDebug(true);
// 关闭调试
cc.dynamicAtlasManager.showDebug(false);

1.9.2 先出自定义查看动态合图_核心代码( Bookmarklet 片段代码):::=>

javascript:(()=>{var getUserConfirm=confirm("是否查看合图?");window.showCodeInfoTime=" 【"+new Date().toLocaleString()+"=>星期"+['天','壹','贰','叁','肆','伍','陆'][new Date().getDay()]+"=>周"+['末','一','二','三','四','五','六'][new Date().getDay()]+"】 ";window.dragBlockOffset=cc.Vec2.ZERO;cc.dynamicAtlasManager.showDebug(getUserConfirm);if(getUserConfirm){var setNew_scroll=new cc.Node();var setNew_cont=new cc.Node();setNew_scroll.name="DYNAMIC_ATLAS_DEBUG_NODE_selfSet";setNew_cont.name="CONTENT";setNew_scroll.anchorX=0.5;setNew_scroll.anchorY=0.5;setNew_scroll.x=0;setNew_scroll.y=0;setNew_scroll.width=cc.winSize.width;setNew_scroll.height=cc.winSize.height;setNew_cont.anchorX=0.5;setNew_cont.anchorY=0.5;setNew_cont.x=0;setNew_cont.y=0;setNew_cont.width=cc.winSize.width;setNew_cont.height=cc.winSize.height;setNew_cont.parent=setNew_scroll;setNew_scroll.parent=cc.director.getScene().getComponentInChildren("cc.Canvas").node;setNew_scroll.zIndex=9;var getDEBUG_NODE=cc.find("DYNAMIC_ATLAS_DEBUG_NODE");var getAtlas=cc.find("DYNAMIC_ATLAS_DEBUG_NODE/CONTENT/ATLAS");var getAtlas2=setNew_cont;if(getAtlas){cc.log(window.showCodeInfoTime+"%c  已将动态合图的图集输出至 Canvas 节点下 => \n Canvas/DYNAMIC_ATLAS_DEBUG_NODE_selfSet/CONTENT/ATLAS \n 可以自由拖动屏幕上的新图集节点 \n 已自动贴边屏幕","font-size:18px;color:yellow;background-color:black;");var ins_atlas=cc.instantiate(getAtlas);ins_atlas.anchorX=0.5;ins_atlas.anchorY=0.5;ins_atlas.addComponent("cc.Widget");var ins_atlasWid=ins_atlas.getComponent("cc.Widget");ins_atlasWid.isAlignTop=true;ins_atlasWid.isAlignLeft=true;ins_atlasWid.top=55;ins_atlasWid.left=55;ins_atlas.on(cc.Node.EventType.TOUCH_START,onTouchStart,this);ins_atlas.on(cc.Node.EventType.TOUCH_MOVE,onTouchMove,this);ins_atlas.on(cc.Node.EventType.TOUCH_END,onTouchEnd,this);ins_atlas.on(cc.Node.EventType.TOUCH_CANCEL,onTouchCancel,this);if(getAtlas2){ins_atlas.parent=getAtlas2;getDEBUG_NODE.active=false}else{ins_atlas.parent=cc.director.getScene().getComponentInChildren("cc.Canvas").node;getDEBUG_NODE.active=false}}else{console.clear();cc.log(window.showCodeInfoTime+"%c  未找到=>动态合图的大贴图图集 (2048 * 2048) ATLAS ","font-size:25px;color:red;background-color:black;");setNew_scroll.parent=null;setNew_scroll.destroy()}}else{cc.log(window.showCodeInfoTime+"%c  已移除当前新增的 ATLAS 的 DYNAMIC_ATLAS_DEBUG_NODE_selfSet 节点","font-size:25px;color:red;background-color:black;");var getAtlas=cc.find("DYNAMIC_ATLAS_DEBUG_NODE");var getAtlas2=cc.director.getScene().getComponentInChildren("cc.Canvas").node.getChildByName("DYNAMIC_ATLAS_DEBUG_NODE_selfSet");if(getAtlas){getAtlas.parent=null;getAtlas.destroy()};if(getAtlas2){getAtlas2.parent=null;getAtlas2.destroy()}};function onTouchStart(event,customEventData){cc.log("拖动合图图片_坐标_onTouchStart=>",[event.target.getPosition().x,event.target.getPosition().y]);const positionInNode=event.target.getParent().convertToNodeSpaceAR(event.getLocation());window.dragBlockOffset=positionInNode.sub(event.target.getPosition())};function onTouchMove(event,customEventData){if(!window.dragBlockOffset){return};const positionInWorld=event.getLocation(),positionInNode=event.target.getParent().convertToNodeSpaceAR(positionInWorld);cc.log("拖动合图图片_坐标_onTouchMove=>",[positionInNode.sub(window.dragBlockOffset).x,positionInNode.sub(window.dragBlockOffset).y]);event.target.setPosition(positionInNode.sub(window.dragBlockOffset))};function onTouchCancel(event,customEventData){cc.log("拖动合图图片_坐标_onTouchCancel=>",[event.getLocation().x,event.getLocation().y]);onTouchEnd(event,"")};function onTouchEnd(event,customEventData){if(!window.dragBlockOffset){return};cc.log("拖动合图图片_坐标_onTouchEnd=>",[event.getLocation().x,event.getLocation().y]);window.dragBlockOffset=null}})();

1.9.3 再出自定义查看动态合图_核心代码( Snip 片段代码):::=>

// 这个头写法就可以省略不写了
// javascript: (()=>{
    var getUserConfirm = confirm("是否查看合图?");
    window.showCodeInfoTime = " 【" + new Date().toLocaleString() + "=>星期" + ['天', '壹', '贰', '叁', '肆', '伍', '陆'][new Date().getDay()] + "=>周" + ['末', '一', '二', '三', '四', '五', '六'][new Date().getDay()] + "】 ";
    window.dragBlockOffset = cc.Vec2.ZERO;
    cc.dynamicAtlasManager.showDebug(getUserConfirm);
    if (getUserConfirm) {
        var setNew_scroll = new cc.Node();
        var setNew_cont = new cc.Node();

        setNew_scroll.name = "DYNAMIC_ATLAS_DEBUG_NODE_selfSet";
        setNew_cont.name = "CONTENT";

        setNew_scroll.anchorX = 0.5;
        setNew_scroll.anchorY = 0.5;
        setNew_scroll.x = 0;
        setNew_scroll.y = 0;
        setNew_scroll.width = cc.winSize.width;
        setNew_scroll.height = cc.winSize.height;

        setNew_cont.anchorX = 0.5;
        setNew_cont.anchorY = 0.5;
        setNew_cont.x = 0;
        setNew_cont.y = 0;
        setNew_cont.width = cc.winSize.width;
        setNew_cont.height = cc.winSize.height;

        setNew_cont.parent = setNew_scroll;
        setNew_scroll.parent = cc.director.getScene().getComponentInChildren("cc.Canvas").node;
        setNew_scroll.zIndex = 9;
        var getDEBUG_NODE = cc.find("DYNAMIC_ATLAS_DEBUG_NODE");
        var getAtlas = cc.find("DYNAMIC_ATLAS_DEBUG_NODE/CONTENT/ATLAS");
        var getAtlas2 = setNew_cont;
        if (getAtlas) {
            cc.log(window.showCodeInfoTime + "%c  已将动态合图的图集输出至 Canvas 节点下 => \n Canvas/DYNAMIC_ATLAS_DEBUG_NODE_selfSet/CONTENT/ATLAS \n 可以自由拖动屏幕上的新图集节点 \n 已自动贴边屏幕", "font-size:18px;color:yellow;background-color:black;");
            var ins_atlas = cc.instantiate(getAtlas);

            ins_atlas.anchorX = 0.5;
            ins_atlas.anchorY = 0.5;
            ins_atlas.addComponent("cc.Widget");
            var ins_atlasWid = ins_atlas.getComponent("cc.Widget");
            ins_atlasWid.isAlignTop = true;
            ins_atlasWid.isAlignLeft = true;
            ins_atlasWid.top = 55;
            ins_atlasWid.left = 55;

            ins_atlas.on(cc.Node.EventType.TOUCH_START, onTouchStart, this);
            ins_atlas.on(cc.Node.EventType.TOUCH_MOVE, onTouchMove, this);
            ins_atlas.on(cc.Node.EventType.TOUCH_END, onTouchEnd, this);
            ins_atlas.on(cc.Node.EventType.TOUCH_CANCEL, onTouchCancel, this);
            if (getAtlas2) {
                ins_atlas.parent = getAtlas2;
                getDEBUG_NODE.active = false;
            } else {
                ins_atlas.parent = cc.director.getScene().getComponentInChildren("cc.Canvas").node;
                getDEBUG_NODE.active = false;
            }
            ;
        } else {
            console.clear();
            cc.log(window.showCodeInfoTime + "%c  未找到=>动态合图的大贴图图集 (2048 * 2048) ATLAS ", "font-size:25px;color:red;background-color:black;");
            setNew_scroll.parent = null;
            setNew_scroll.destroy();
        }
        ;
    } else {
        cc.log(window.showCodeInfoTime + "%c  已移除当前新增的 ATLAS 的 DYNAMIC_ATLAS_DEBUG_NODE_selfSet 节点", "font-size:25px;color:red;background-color:black;");
        var getAtlas = cc.find("DYNAMIC_ATLAS_DEBUG_NODE");
        var getAtlas2 = cc.director.getScene().getComponentInChildren("cc.Canvas").node.getChildByName("DYNAMIC_ATLAS_DEBUG_NODE_selfSet");
        if (getAtlas) {
            getAtlas.parent = null;
            getAtlas.destroy();
        }
        ;if (getAtlas2) {
            getAtlas2.parent = null;
            getAtlas2.destroy();
        }
        ;
    }
    ;function onTouchStart(event, customEventData) {
        cc.log("拖动合图图片_坐标_onTouchStart=>", [event.target.getPosition().x, event.target.getPosition().y]);
        const positionInNode = event.target.getParent().convertToNodeSpaceAR(event.getLocation());
        window.dragBlockOffset = positionInNode.sub(event.target.getPosition());
    }
    ;function onTouchMove(event, customEventData) {
        if (!window.dragBlockOffset) {
            return;
        }
        ;const positionInWorld = event.getLocation()
          , positionInNode = event.target.getParent().convertToNodeSpaceAR(positionInWorld);
        cc.log("拖动合图图片_坐标_onTouchMove=>", [positionInNode.sub(window.dragBlockOffset).x, positionInNode.sub(window.dragBlockOffset).y]);
        event.target.setPosition(positionInNode.sub(window.dragBlockOffset));
    }
    ;function onTouchCancel(event, customEventData) {
        cc.log("拖动合图图片_坐标_onTouchCancel=>", [event.getLocation().x, event.getLocation().y]);
        onTouchEnd(event, "");
    }
    ;function onTouchEnd(event, customEventData) {
        if (!window.dragBlockOffset) {
            return;
        }
        ;cc.log("拖动合图图片_坐标_onTouchEnd=>", [event.getLocation().x, event.getLocation().y]);
        window.dragBlockOffset = null;
    }
    ;

// })();

1.9.4 自定义输出 FPS 帧率日志( Snip 片段代码):::=>

// javascript: (()=>{
    var getShowFps = confirm("是否实时输出 FPS 帧率日志?");
    if (!getShowFps) {
        console.log("%c 取消输出 FPS 帧率日志", "background-color:#040;color:#f1e75a !important;font-size:calc(64px/2);");
        // return;
    }else{
    ;var timerNum = 0
      , tempShowNum = 100;
    var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
        window.setTimeout(callback, 1000 / 60);
    }
    ;
    var e, pe, pid, fps, last, offset, step, appendFps;
    fps = 0;
    last = Date.now();
    step = function() {
        offset = Date.now() - last;
        fps += 1;
        if (offset >= 1000) {
            last += offset;
            appendFps(fps);
            fps = 0;
        }
        requestAnimationFrame(step);
    }
    ;
    appendFps = function(fps) {
        if (timerNum > tempShowNum) {
            timerNum = 0;
            console.clear();
        }
        ;timerNum++;
        console.log('%c 当前页面的帧率::' + timerNum + '=>\n ' + fps + ' FPS', "background-color:#000;color:#f1e05a !important;font-size:calc(55px/2);");
    }
    ;
    step();
};
// })();

1.9.5 自定义场景内节点的 Label Cache Mode 类型_核心代码( Bookmarklet 片段代码):::=>

这个 getComponentInChildren 代码也可以使用

// 先写 2.x 的::
javascript:var modeType=prompt("自定义 2.x 的 Label Cache Mode 类型,0-2",1);cc.director.getScene().children[0].getComponentsInChildren(cc.Label).forEach((nod)=>{nod.cacheMode=modeType;});

// 这个是 3.x 的::
javascript:var modeType=prompt("自定义 3.x 的 Label Cache Mode 类型,0-2",1);cc.director.getScene().getChildByName("Canvas").getComponentsInChildren(cc.LabelComponent).forEach((nod)=>{nod.cacheMode=modeType;});

1.9.6 自定义场景内节点的 Label Cache Mode 类型_核心代码( Snip 片段代码):::=>

关键使用的是 getComponentsInChildren 代码

  • 其实, getComponentsInChildren 可以用在很多的场景里面, 大家可以自行研究
// 先写 2.x 的::
// javascript: 
var modeType = prompt("自定义 2.x 的 Label Cache Mode 类型,0-2", 1);
cc.director.getScene().children[0].getComponentsInChildren(cc.Label).forEach((nod)=>{
    nod.cacheMode = modeType;
});

// 这个是 3.x 的::
// javascript: 
var modeType = prompt("自定义 3.x 的 Label Cache Mode 类型,0-2", 1);
cc.director.getScene().getChildByName("Canvas").getComponentsInChildren(cc.LabelComponent).forEach((nod)=>{
    nod.cacheMode = modeType;
});

2. :dog2: 在 Cocos 内用代码写最简单的 WebGL图形(Ts) + 超小杯 [付费插件] :cat2:

2.1 直接出核心代码( html 写法代码):::=>

// Cocos 的写法请参考插件内的代码
// 简单调用 GL 绘制画布变成红色
const gl0 = document.querySelector('canvas').getContext('webgl');
gl0.clearColor(1, 0, 0, 1); 
gl0.clear(gl0.COLOR_BUFFER_BIT);

// 简单绘制多个不同颜色的矩形
const gl = document.querySelector('#c').getContext('webgl'); 
gl.enable(gl.SCISSOR_TEST); 
function drawRect(x, y, width, height, color) {
  gl.scissor(x, y, width, height);
  gl.clearColor(...color);
  gl.clear(gl.COLOR_BUFFER_BIT);
}; 
for (let i = 0; i < 100; ++)) {
  const x = rand(0, 300);
  const y = rand(0, 150);
  const width = rand(0, 300 - x);
  const height = rand(0, 150 - y);
  drawRect(x, y, width, height, [rand(1), rand(1), rand(1), 1]);
}; 
function rand(min, max) {
  if (max === undefined) {
    max = min;
    min = 0;
  }
  return Math.random() * (max - min) + min;
};
参考:: WebGL 最小的程序 (webglfundamentals.org)
// 简单来说,就是获取一下 GL ,然后调用 WebGL 的代码绘制小矩形
2.2 看看这个简单的 十万(1000*100)++ 往上的不断重绘 WebGL 图, 可以知道, Cocos 会越来越好:star:

3. RVO 避障的简单理解 + [免费源码] :star:

  • 本条目行文参考论坛代码如下

3.1.1 半参考(仅作研究) 金币落袋效果
3.1.2 半参考(仅作研究) 50 行代码,优化列表draw call
3.1.3 半参考(仅作研究) 50 行写个简单的状态机

原理解析: 简略来说,就是一个圆周运动计算后,中点 坐标偏移
简单来说: 避障, 就是一个方向+速度的一个坐标计算的过程

3.2 RVO 避障的简单理解效果展示

3.2.1 开启此效果


3.2.2 场景内拖动节点后的效果

本文总结:::=>

想说的太多, 说的多了就成废话了, 因为本人懂得太少, 所以, 都在代码里了 :elephant:

小疑惑:::=>

各位大佬, 请问 3.x 的插件开发还有比较好的能参考的地方吗? :confused:
比如 2.x 插件可以自由读取场景内的节点数据并操作, 还有 run-time 展示插件内的 ts 代码.
请问 3.x 的这两个功能有什么地方说的比较详细的吗 ? :face_with_monocle:
在下才疏学浅, 多有不足之处, 还请各位大佬多多见谅, 欢迎提出您宝贵的见解, 感谢 :smiley:

10赞

:star: 在下才疏学浅, 多有不足之处, 还请各位大佬多多见谅, 欢迎提出您宝贵的见解, 感谢 :smiley:

好~好长啊~ :laughing:

1赞

啊!看不懂啊~

:joy:请问是有什么地方写的太复杂了吗?我再改改?

啊!不不不,是我水平比较次……

:rofl: 大佬太谦虚了, 我其实很菜的

一脸懵逼进来一辆懵逼出去,这是整理文?还是啥?这题目长得吓人。。。。还以为啥高大的技术分享

多个主题就分开写吧?

来龙去脉交待清楚好些

:sweat_smile: :sweat_smile:不是高大上的文章, 就是些简单的内容,
目前文章还在补完中, 后续会继续完善下本文的内容,
尽量争取让对 BookMarklet不太明白的朋友, 在看了本文后可以自己写或者使用这一块的内容.

:thinking:
拆分成三四个主题吗? 好像这个文章内容不是很多的话, 拆分会不会可读内容就比较太少了 ?
大佬您看, 加了个目录是不是要清晰点?

你这有点像官方文档,没有阅读兴趣

好的, 后续再改改, 开始的时候确实是有点想写个类似文档(附带代码)之类的风格

哈哈,之前写了一个饿了吗拼单插件,用来计算每个人要出多少钱,就是用 bookmarklet 干的

1赞

哈哈,我在你的git看过这个插件的代码,属实厉害

1赞

:smile::+1:
确实, 感觉这个 Bookmarklet 和 Snip 代码片段使用比较方便, 而且在浏览器上调试也很方便