Agar 流体效果的 Cocos2d-JS 实现

前端之家收集整理的这篇文章主要介绍了Agar 流体效果的 Cocos2d-JS 实现前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

代码取自 Agar 原版源码,移植到 Cocos2d-JS v3.7 上。 使用方法,新建一个 HelloWorld 工程,将以下代码覆盖新创建的工程的 app.js 文件中的代码即可看到效果代码中已经有充分的注释说明。 // by WM, QQ: 348915654 // PosNode 为实际的点 // AreaNode 为表示空间的点 var g_versionStr = "v1.0"; //var g_timestamp = +new Date; // 时间戳,原本用于细胞的旋转,目前用不到 // 墙壁的最小点和最大点 // 坐标系为 →x ↑y var g_leftPos = 50; // min x var g_bottomPos = 50; // min y var g_rightPos = 600; // max x var g_topPos = 600; // max y // 四叉树 var Quad = { init: function (args) { function Node(x, y, w, h, depth) { this.x = x; this.y = y; this.w = w; this.h = h; this.depth = depth; // 当前的深度 this.items = []; // 实际存储的位置节点 // Array<PosNode> this.nodes = []; // 用于分割空间的节点,简称空间节点 // Array<AreaNode> } var m_maxChildren = args.maxChildren || 2, m_maxDepth = args.maxDepth || 4; Node.prototype = { x: 0, y: 0, w: 0, h: 0, depth: 0, items: null, nodes: null, // selector: {x: Number, y: Number, w: Number, h: Number} // 判断 selector 所表示的区域内有没有 PosNode,如果有则返回 true,否则返回 false exists: function (selector) { for (var i = 0; i < this.items.length; ++i) { var item = this.items[i]; if (item.x >= selector.x && item.y >= selector.y && item.x < selector.x + selector.w && item.y < selector.y + selector.h) return true } if (0 != this.nodes.length) { var self = this; return this.findOverlappingNodes(selector, function (dir) { return self.nodes[dir].exists(selector) }) } return false; }, // item: {x: Number, y: Number, w: Number, h: Number}; callback: function (PosNode); // 判断 item 所表示的区域内有没有 PosNode,并对所有符合条件的 PosNode 依次调用 callback() 函数 retrieve: function (item, callback) { for (var i = 0; i < this.items.length; ++i) callback(this.items[i]); if (0 != this.nodes.length) { var self = this; this.findOverlappingNodes(item, function (dir) { self.nodes[dir].retrieve(item, callback) }) } }, // a: {x: Number, y: Number} insert: function (a) { if (0 != this.nodes.length) { this.nodes[this.findInsertNode(a)].insert(a); } else { // 如果这个节点下的位置节点过多且未达到最大深度,则将这个节点分割,将这些位置节点放入分割的节点中 if (this.items.length >= m_maxChildren && this.depth < m_maxDepth) { this.devide(); this.nodes[this.findInsertNode(a)].insert(a); } else { this.items.push(a); } } }, // 找这个 a 节点属于这个 this 的哪个象限 // 空间区域号 airId, →x ↑y, 左下为 0,左上为 2,右下为 1,右上为 3 findInsertNode: function (a) { return a.x < this.x + this.w / 2 ? a.y < this.y + this.h / 2 ? 0 : 2 : a.y < this.y + this.h / 2 ? 1 : 3; }, // a: {x: Number, y: Number, w: Number, h: Number}; b: function (dir); // 这个函数的操作是判断 a 所表示的区域是哪一块就调用哪个 b(airId) 并在最后返回一个布尔值 findOverlappingNodes: function (a, b) { return a.x < this.x + this.w / 2 && (a.y < this.y + this.h / 2 && b(0) || a.y >= this.y + this.h / 2 && b(2)) || a.x >= this.x + this.w / 2 && (a.y < this.y + this.h / 2 && b(1) || a.y >= this.y + this.h / 2 && b(3)) ? true : false; }, devide: function () { var a = this.depth + 1, c = this.w / 2, d = this.h / 2; this.nodes.push(new Node(this.x, this.y, c, d, a)); this.nodes.push(new Node(this.x + c, this.y, c, d, a)); this.nodes.push(new Node(this.x, this.y + d, c, d, a)); this.nodes.push(new Node(this.x + c, this.y + d, c, d, a)); a = this.items; this.items = []; for (c = 0; c < a.length; c++) this.insert(a[c]); }, clear: function () { for (var a = 0; a < this.nodes.length; a++) this.nodes[a].clear(); this.items.length = 0; this.nodes.length = 0; } }; var internalSelector = { x: 0, y: 0, w: 0, h: 0 }; return { root: new Node(args.minX, args.minY, args.maxX - args.minX, args.maxY - args.minY, 0), insert: function (a) { this.root.insert(a); }, retrieve: function (a, b) { this.root.retrieve(a, b); }, retrieve2: function (a, b, c, d, callback) { internalSelector.x = a; internalSelector.y = b; internalSelector.w = c; internalSelector.h = d; this.root.retrieve(internalSelector, callback); }, exists: function (a) { return this.root.exists(a); }, clear: function () { this.root.clear(); } } } }; // 用于保存四叉树的全局变量 var g_qTree = null; // 细胞 var Cell = cc.Class.extend({ m_id: 0, m_x: 0, m_y: 0, m_size: 100, // 细胞的半径 m_pointsNum: 128, // 点的数量 m_points: null, // @private m_pointsAcc: null, // @private m_isVirus: false, // 如果为 true 细胞将呈现锯齿状 m_isAgitated: false, // 如果是激活状态细胞将振动得更加激烈 m_drawNode: null, ctor: function () { this.m_points = []; this.m_pointsAcc = []; this.CreatePoints(); }, Destroy: function () { this.m_points = null; this.m_pointsAcc = null; }, GetNumPoints: function () { return this.m_pointsNum; }, CreatePoints: function () { var sampleNum = 0; var rand = 0; var rand2 = 0; var point = null; sampleNum = this.GetNumPoints(); for ( ; this.m_points.length > sampleNum; ) { rand = ~~(Math.random() * this.m_points.length); this.m_points.splice(rand, 1); this.m_pointsAcc.splice(rand, 1); } if (this.m_points.length == 0 && sampleNum > 0) { this.m_points.push({ ref: this, size: this.m_size, x: this.m_x, y: this.m_y }); this.m_pointsAcc.push(Math.random() - 0.5); } while (this.m_points.length < sampleNum) { rand2 = ~~(Math.random() * this.m_points.length); point = this.m_points[rand2]; this.m_points.splice(rand2, 0, { ref: this, size: point.size, x: point.x, y: point.y }); this.m_pointsAcc.splice(rand2, 0, this.m_pointsAcc[rand2]); } }, MovePoints: function () { this.CreatePoints(); var points = null; var pointsAcc = null; var numPoints = 0; var i = 0; var posAcc1 = 0; var posAcc2 = 0; var ref = null; var isVirus = 0; var j = 0; var f = 0; var e = 0; var m = 0; var isCollide = false; var posX = 0; var posY = 0; for (points = this.m_points, pointsAcc = this.m_pointsAcc, numPoints = points.length, i = 0; i < numPoints; i++) { posAcc1 = pointsAcc[(i - 1 + numPoints) % numPoints]; posAcc2 = pointsAcc[(i + 1 + numPoints) % numPoints]; pointsAcc[i] += (Math.random() - 0.5) * (this.m_isAgitated ? 3 : 1); // 半径随机的变化从这里来 pointsAcc[i] *= 0.7; if (pointsAcc[i] > 10) { pointsAcc[i] = 10; } if (pointsAcc[i] < -10) { pointsAcc[i] = 10; } pointsAcc[i] = (posAcc1 + posAcc2 + 8 * pointsAcc[i]) / 10; } // isVirus 用于这个循环的累加角度,如果是病毒则不计算累加角度 // for (ref = this, isVirus = this.m_isVirus ? 0 : (this.m_id / 1000 + g_timestamp / 10000) % (2 * Math.PI), j = 0; j < numPoints; j++) { for (ref = this, j = 0; j < numPoints; j++) { f = points[j].size; e = points[(j - 1 + numPoints) % numPoints].size; m = points[(j + 1 + numPoints) % numPoints].size; { // 判断是否与其它点产生碰撞 //... isCollide = false; posX = points[j].x; posY = points[j].y; // 与其它节点的碰撞 //... if (g_qTree != null) { g_qTree.retrieve2(posX - 5, posY - 5, 10, 10, function (node) { if (node.ref != ref && ((posX - node.x) * (posX - node.x) + (posY - node.y) * (posY - node.y) < 25)) { isCollide = true; } }) } // 与墙壁的碰撞 if (!isCollide && posX < g_leftPos || posX > g_rightPos || posY < g_bottomPos || posY > g_topPos) { isCollide = true; } if (isCollide) { if (pointsAcc[j] > 0) { pointsAcc[j] = 0; } pointsAcc[j] -= 1; } } f += pointsAcc[j]; // 半径变化 if (f < 0) { f = 0; } f = this.m_isAgitated ? (19 * f + this.m_size) / 20 : (12 * f + this.m_size) / 13; // 半径向 this.m_size 变化 points[j].size = (e + m + 8 * f) / 10; // 半径根据两旁的半径来变化 // 两旁的各占一份,自己占八份 e = 2 * Math.PI / numPoints; // 单份的角度 m = this.m_points[j].size; // 半径 // 如果是病毒还可以呈现出锯齿效果 if (this.m_isVirus && 0 == j % 2) { m += 5; } // 这是圆的公式 points[j].x = this.m_x + Math.cos(e * j + isVirus) * m; points[j].y = this.m_y + Math.sin(e * j + isVirus) * m; } }, ShouldRender: function () { return true; }, DrawOneCell: function (drawNode) { if (this.ShouldRender()) { this.MovePoints(); var d = this.GetNumPoints(); var c = 0; var e = 0; var verts = []; for (c = 1; c <= d; c++) { e = c % d; verts.push(cc.p(this.m_points[e].x, this.m_points[e].y)); } drawNode.clear(); drawNode.drawPoly(verts, cc.color.WHITE, 2, cc.color.BLACK); } }, HaHaTest: function () { } }); var g_mainLogic = null; var HelloWorldLayer = cc.Layer.extend({ sprite:null, m_cells: null, ctor:function () { ////////////////////////////// // 1. super init first this._super(); ///////////////////////////// // 2. add a menu item with "X" image, which is clicked to quit the program // you may modify it. // ask the window size var size = cc.winSize; // add a "close" icon to exit the progress. it's an autorelease object var closeItem = new cc.MenuItemImage( res.CloseNormal_png, res.CloseSelected_png, function () { cc.log("Menu is clicked!"); }, this); closeItem.attr({ x: size.width - 20, y: 20, anchorX: 0.5, anchorY: 0.5 }); var menu = new cc.Menu(closeItem); menu.x = 0; menu.y = 0; this.addChild(menu, 1); ///////////////////////////// // 3. add your codes below... // add a label shows "Hello World" // create and initialize a label var helloLabel = new cc.LabelTTF(g_versionStr, "Arial", 38); // position the label on the center of the screen helloLabel.x = size.width / 2; helloLabel.y = 0; // add the label as a child to this layer this.addChild(helloLabel, 5); // add "HelloWorld" splash screen" this.sprite = new cc.Sprite(res.HelloWorld_png); this.sprite.attr({ x: size.width / 2, y: size.height / 2, scale: 0.5, rotation: 180 }); this.addChild(this.sprite, 0); this.sprite.runAction( cc.sequence( cc.rotateTo(2, 0), cc.scaleTo(2, 1, 1) ) ); helloLabel.runAction( cc.spawn( cc.moveBy(2.5, cc.p(0, size.height - 40)), cc.tintTo(2.5,255,125,0) ) ); this.scheduleUpdate(); g_mainLogic = this; this.MyInit(); return true; }, update: function (dt) { this._super(dt); var cell = null; // g_timestamp = +new Date; // +new Date 为新的时间戳 this.BuildQTree(); // 构造四叉树,每次循环都要重新构造一遍 for (var i = 0; i < this.m_cells.length; i++) { cell = this.m_cells[i]; cell.DrawOneCell(cell.m_drawNode); // 更新细胞的节点位置和绘制细胞 } }, BuildQTree: function () { var a = Number.POSITIVE_INFINITY; // minX var b = Number.POSITIVE_INFINITY; // minY var c = Number.NEGATIVE_INFINITY; // maxX var d = Number.NEGATIVE_INFINITY; // maxY var e = 0; // maxRadius var cell = null; var posX = 0; var posY = 0; for (var i = 0; i < this.m_cells.length; i++) { cell = this.m_cells[i]; if (cell.ShouldRender()) { // 判断条件还会扩展 //... a = Math.min(cell.m_x, a); b = Math.min(cell.m_y, b); c = Math.max(cell.m_x, c); d = Math.max(cell.m_y, d); e = Math.max(cell.m_size, e); } } g_qTree = Quad.init({ minX: a - (e + 100), minY: b - (e + 100), maxX: c + (e + 100), maxY: d + (e + 100), maxChildren: 2, maxDepth: 4 }); for (var i = 0; i < this.m_cells.length; i++) { cell = this.m_cells[i]; if (cell.ShouldRender()) { // 判断条件还会扩展 //... for (var j = 0; j < cell.m_points.length; j++) { posX = cell.m_points[j].x; posY = cell.m_points[j].y; if (true) { // 判断条件还会扩展 //... g_qTree.insert(cell.m_points[j]); } } } } }, MyInit: function () { var winSize = cc.winSize; var cell = null; var drawNode = null; this.m_cells = []; // 创建一个病毒 cell = new Cell(); cell.m_isVirus = true; cell.m_size = 100; cell.m_pointsNum = 128; cell.m_x = 120; cell.m_y = 120; drawNode = new cc.DrawNode(); this.addChild(drawNode, 100); cell.m_drawNode = drawNode; this.m_cells.push(cell); cell = new Cell(); cell.m_isVirus = false; cell.m_size = 50; cell.m_pointsNum = 64; cell.m_x = 240; cell.m_y = 80; drawNode = new cc.DrawNode(); this.addChild(drawNode, 90); cell.m_drawNode = drawNode; this.m_cells.push(cell); cell = new Cell(); cell.m_isVirus = false; cell.m_size = 120; cell.m_pointsNum = 128; cell.m_x = 400; cell.m_y = 300; drawNode = new cc.DrawNode(); this.addChild(drawNode, 110); cell.m_drawNode = drawNode; this.m_cells.push(cell); cell = new Cell(); cell.m_isVirus = false; cell.m_size = 50; cell.m_pointsNum = 64; cell.m_x = 400; cell.m_y = 160; drawNode = new cc.DrawNode(); this.addChild(drawNode, 90); cell.m_drawNode = drawNode; this.m_cells.push(cell); } }); var HelloWorldScene = cc.Scene.extend({ onEnter:function () { this._super(); var layer = new HelloWorldLayer(); this.addChild(layer); } });

原文链接:https://www.f2er.com/cocos2dx/341749.html

猜你在找的Cocos2d-x相关文章