JS实现基于Sketch.js模拟成群游动的蝌蚪运动动画效果【附demo源码下载】


Posted in Javascript onAugust 18, 2017

本文实例讲述了JS实现基于Sketch.js模拟成群游动的蝌蚪运动动画效果。分享给大家供大家参考,具体如下:

基于Sketch.js,实现了物体触碰检测(蝌蚪会遇到障碍物以及聪明的躲避鼠标的点击),随机运动,聚集算法等。

已经具备了游戏的基本要素,扩展一下可以变成一个不错的 HTML5 游戏。

演示效果如下:

JS实现基于Sketch.js模拟成群游动的蝌蚪运动动画效果【附demo源码下载】

完整代码如下:

<!DOCTYPE html>
<html class=" -webkit- js flexbox canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths">
<head>
<meta charset="UTF-8">
<title>HTML5 Preview Panel</title>
<script src="prefixfree.min.js"></script>
<script src="modernizr.js"></script>
<style>
body {
  background-color: #222;
}
</style>
</head>
<body>
<script src="sketch.min.js"></script>
<div id="container">
<canvas class="sketch" id="sketch-0" height="208" width="607"></canvas>
</div>
<script>
var calculateDistance = function(object1, object2) {
  x = Math.abs(object1.x - object2.x);
  y = Math.abs(object1.y - object2.y);
  return Math.sqrt((x * x) + (y * y));
};
var calcMagnitude = function(x, y) {
  return Math.sqrt((x * x) + (y * y));
};
var calcVectorAdd = function(v1, v2) {
  return {
    x: v1.x + v2.x,
    y: v1.y + v2.y
  };
};
var random = function(min, max) {
  return min + Math.random() * (max - min);
};
var getRandomItem = function(list, weight) {
  var total_weight = weight.reduce(function(prev, cur, i, arr) {
    return prev + cur;
  });
  var random_num = random(0, total_weight);
  var weight_sum = 0;
  //console.log(random_num)
  for (var i = 0; i < list.length; i++) {
    weight_sum += weight[i];
    weight_sum = +weight_sum.toFixed(2);
    if (random_num <= weight_sum) {
      return list[i];
    }
  }
  // end of function
};
/***********************
BOID
***********************/
function Boid(x, y) {
  this.init(x, y);
}
Boid.prototype = {
  init: function(x, y) {
    //body
    this.type = "boid";
    this.alive = true;
    this.health = 1;
    this.maturity = 4;
    this.speed = 6;
    this.size = 5;
    this.hungerLimit = 12000;
    this.hunger = 0;
    this.isFull = false;
    this.digestTime = 400;
    this.color = 'rgb(' + ~~random(0, 100) + ',' + ~~random(50, 220) + ',' + ~~random(50, 220) + ')';
    //brains
    this.eyesight = 100; //range for object dectection
    this.personalSpace = 20; //distance to avoid safe objects
    this.flightDistance = 60; //distance to avoid scary objects
    this.flockDistance = 100; //factor that determines how attracted the boid is to the center of the flock
    this.matchVelFactor = 6; //factor that determines how much the flock velocity affects the boid. less = more matching
    this.x = x || 0.0;
    this.y = y || 0.0;
    this.v = {
      x: random(-1, 1),
      y: random(-1, 1),
      mag: 0
    };
    this.unitV = {
      x: 0,
      y: 0,
    };
    this.v.mag = calcMagnitude(this.v.x, this.v.y);
    this.unitV.x = (this.v.x / this.v.mag);
    this.unitV.y = (this.v.y / this.v.mag);
  },
  wallAvoid: function(ctx) {
    var wallPad = 10;
    if (this.x < wallPad) {
      this.v.x = this.speed;
    } else if (this.x > ctx.width - wallPad) {
      this.v.x = -this.speed;
    }
    if (this.y < wallPad) {
      this.v.y = this.speed;
    } else if (this.y > ctx.height - wallPad) {
      this.v.y = -this.speed;
    }
  },
  ai: function(boids, index, ctx) {
    percievedCenter = {
      x: 0,
      y: 0,
      count: 0
    };
    percievedVelocity = {
      x: 0,
      y: 0,
      count: 0
    };
    mousePredator = {
      x: ((typeof ctx.touches[0] === "undefined") ? 0 : ctx.touches[0].x),
      y: ((typeof ctx.touches[0] === "undefined") ? 0 : ctx.touches[0].y)
    };
    for (var i = 0; i < boids.length; i++) {
      if (i != index) {
        dist = calculateDistance(this, boids[i]);
        //Find all other boids close to it
        if (dist < this.eyesight) {
          //if the same species then flock
          if (boids[i].type == this.type) {
            //Alignment
            percievedCenter.x += boids[i].x;
            percievedCenter.y += boids[i].y;
            percievedCenter.count++;
            //Cohesion
            percievedVelocity.x += boids[i].v.x;
            percievedVelocity.y += boids[i].v.y;
            percievedVelocity.count++;
            //Separation
            if (dist < this.personalSpace + this.size + this.health) {
              this.avoidOrAttract("avoid", boids[i]);
            }
          } else {
            //if other species fight or flight
            if (dist < this.size + this.health + boids[i].size + boids[i].health) {
              this.eat(boids[i]);
            } else {
              this.handleOther(boids[i]);
            }
          }
        } //if close enough
      } //dont check itself
    } //Loop through boids
    //Get the average for all near boids
    if (percievedCenter.count > 0) {
      percievedCenter.x = ((percievedCenter.x / percievedCenter.count) - this.x) / this.flockDistance;
      percievedCenter.y = ((percievedCenter.y / percievedCenter.count) - this.y) / this.flockDistance;
      this.v = calcVectorAdd(this.v, percievedCenter);
    }
    if (percievedVelocity.count > 0) {
      percievedVelocity.x = ((percievedVelocity.x / percievedVelocity.count) - this.v.x) / this.matchVelFactor;
      percievedVelocity.y = ((percievedVelocity.y / percievedVelocity.count) - this.v.y) / this.matchVelFactor;
      this.v = calcVectorAdd(this.v, percievedVelocity);
    }
    //Avoid Mouse
    if (calculateDistance(mousePredator, this) < this.eyesight) {
      var mouseModifier = 20;
      this.avoidOrAttract("avoid", mousePredator, mouseModifier);
    }
    this.wallAvoid(ctx);
    this.limitVelocity();
  },
  setUnitVector: function() {
    var magnitude = calcMagnitude(this.v.x, this.v.y);
    this.v.x = this.v.x / magnitude;
    this.v.y = this.v.y / magnitude;
  },
  limitVelocity: function() {
    this.v.mag = calcMagnitude(this.v.x, this.v.y);
    this.unitV.x = (this.v.x / this.v.mag);
    this.unitV.y = (this.v.y / this.v.mag);
    if (this.v.mag > this.speed) {
      this.v.x = this.unitV.x * this.speed;
      this.v.y = this.unitV.y * this.speed;
    }
  },
  avoidOrAttract: function(action, other, modifier) {
    var newVector = {
      x: 0,
      y: 0
    };
    var direction = ((action === "avoid") ? -1 : 1);
    var vModifier = modifier || 1;
    newVector.x += ((other.x - this.x) * vModifier) * direction;
    newVector.y += ((other.y - this.y) * vModifier) * direction;
    this.v = calcVectorAdd(this.v, newVector);
  },
  move: function() {
    this.x += this.v.x;
    this.y += this.v.y;
    if (this.v.mag > this.speed) {
      this.hunger += this.speed;
    } else {
      this.hunger += this.v.mag;
    }
  },
  eat: function(other) {
    if (!this.isFull) {
      if (other.type === "plant") {
        other.health--;
        this.health++;
        this.isFull = true;
        this.hunger = 0;
      }
    }
  },
  handleOther: function(other) {
    if (other.type === "predator") {
      this.avoidOrAttract("avoid", other);
    }
  },
  metabolism: function() {
    if (this.hunger >= this.hungerLimit) {
      this.health--;
      this.hunger = 0;
    }
    if (this.hunger >= this.digestTime) {
      this.isFull = false;
    }
    if (this.health <= 0) {
      this.alive = false;
    }
  },
  mitosis: function(boids) {
    if (this.health >= this.maturity) {
      //reset old boid
      this.health = 1;
      birthedBoid = new Boid(
        this.x + random(-this.personalSpace, this.personalSpace),
        this.y + random(-this.personalSpace, this.personalSpace)
      );
      birthedBoid.color = this.color;
      boids.push(birthedBoid);
    }
  },
  draw: function(ctx) {
    drawSize = this.size + this.health;
    ctx.beginPath();
    ctx.moveTo(this.x + (this.unitV.x * drawSize), this.y + (this.unitV.y * drawSize));
    ctx.lineTo(this.x + (this.unitV.y * drawSize), this.y - (this.unitV.x * drawSize));
    ctx.lineTo(this.x - (this.unitV.x * drawSize * 2), this.y - (this.unitV.y * drawSize * 2));
    ctx.lineTo(this.x - (this.unitV.y * drawSize), this.y + (this.unitV.x * drawSize));
    ctx.lineTo(this.x + (this.unitV.x * drawSize), this.y + (this.unitV.y * drawSize));
    ctx.fillStyle = this.color;
    ctx.shadowBlur = 20;
    ctx.shadowColor = this.color;
    ctx.fill();
  }
};
Predator.prototype = new Boid();
Predator.prototype.constructor = Predator;
Predator.constructor = Boid.prototype.constructor;
function Predator(x, y) {
  this.init(x, y);
  this.type = "predator";
  //body
  this.maturity = 6;
  this.speed = 6;
  this.hungerLimit = 25000;
  this.color = 'rgb(' + ~~random(100, 250) + ',' + ~~random(10, 30) + ',' + ~~random(10, 30) + ')';
  //brains
  this.eyesight = 150;
  this.flockDistance = 300;
}
Predator.prototype.eat = function(other) {
  if (!this.isFull) {
    if (other.type === "boid") {
      other.health--;
      this.health++;
      this.isFull = true;
      this.hunger = 0;
    }
  }
};
Predator.prototype.handleOther = function(other) {
  if (other.type === "boid") {
    if (!this.isFull) {
      this.avoidOrAttract("attract", other);
    }
  }
};
Predator.prototype.mitosis = function(boids) {
  if (this.health >= this.maturity) {
    //reset old boid
    this.health = 1;
    birthedBoid = new Predator(
      this.x + random(-this.personalSpace, this.personalSpace),
      this.y + random(-this.personalSpace, this.personalSpace)
    );
    birthedBoid.color = this.color;
    boids.push(birthedBoid);
  }
};
Plant.prototype = new Boid();
Plant.prototype.constructor = Plant;
Plant.constructor = Boid.prototype.constructor;
function Plant(x, y) {
  this.init(x, y);
  this.type = "plant";
  //body
  this.speed = 0;
  this.size = 10;
  this.health = ~~random(1, 10);
  this.color = 'rgb(' + ~~random(130, 210) + ',' + ~~random(40, 140) + ',' + ~~random(160, 220) + ')';
  //brains
  this.eyesight = 0;
  this.flockDistance = 0;
  this.eyesight = 0; //range for object dectection
  this.personalSpace = 100; //distance to avoid safe objects
  this.flightDistance = 0; //distance to avoid scary objects
  this.flockDistance = 0; //factor that determines how attracted the boid is to the center of the flock
  this.matchVelFactor = 0; //factor that determines how much the flock velocity affects the boid
}
Plant.prototype.ai = function(boids, index, ctx) {};
Plant.prototype.move = function() {};
Plant.prototype.mitosis = function(boids) {
  var growProbability = 1,
    maxPlants = 40,
    plantCount = 0;
  for (m = boids.length - 1; m >= 0; m--) {
    if (boids[m].type === "plant") {
      plantCount++;
    }
  }
  if (plantCount <= maxPlants) {
    if (random(0, 100) <= growProbability) {
      birthedBoid = new Plant(
        this.x + random(-this.personalSpace, this.personalSpace),
        this.y + random(-this.personalSpace, this.personalSpace)
      );
      birthedBoid.color = this.color;
      boids.push(birthedBoid);
    }
  }
};
Plant.prototype.draw = function(ctx) {
  var drawSize = this.size + this.health;
  ctx.fillStyle = this.color;
  ctx.shadowBlur = 40;
  ctx.shadowColor = this.color;
  ctx.fillRect(this.x - drawSize, this.y + drawSize, drawSize, drawSize);
};
/***********************
SIM
***********************/
var boids = [];
var sim = Sketch.create({
  container: document.getElementById('container')
});
sim.setup = function() {
  for (i = 0; i < 50; i++) {
    x = random(0, sim.width);
    y = random(0, sim.height);
    sim.spawn(x, y);
  }
};
sim.spawn = function(x, y) {
  var predatorProbability = 0.1,
    plantProbability = 0.3;
  switch (getRandomItem(['boid', 'predator', 'plant'], [1 - predatorProbability - plantProbability, predatorProbability, plantProbability])) {
    case 'predator':
      boid = new Predator(x, y);
      break;
    case 'plant':
      boid = new Plant(x, y);
      break;
    default:
      boid = new Boid(x, y);
      break;
  }
  boids.push(boid);
};
sim.update = function() {
  for (i = boids.length - 1; i >= 0; i--) {
    if (boids[i].alive) {
      boids[i].ai(boids, i, sim);
      boids[i].move();
      boids[i].metabolism();
      boids[i].mitosis(boids);
    } else {
      //remove dead boid
      boids.splice(i, 1);
    }
  }
};
sim.draw = function() {
  sim.globalCompositeOperation = 'lighter';
  for (i = boids.length - 1; i >= 0; i--) {
    boids[i].draw(sim);
  }
  sim.fillText(boids.length, 20, 20);
};
</script>
</body>
</html>

附:完整实例代码点击此处本站下载

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
非常有用的40款jQuery 插件推荐(系列二)
Dec 25 Javascript
Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结
Nov 14 Javascript
jQuery实现提示密码强度的代码
Jul 15 Javascript
九种原生js动画效果
Nov 11 Javascript
JS组件Bootstrap实现弹出框和提示框效果代码
Dec 08 Javascript
详解JavaScript正则表达式之RegExp对象
Dec 13 Javascript
javascript求日期差的方法
Mar 02 Javascript
jQuery实现带水平滑杆的焦点图动画插件
Mar 08 Javascript
jQuery插件Flexslider实现图片轮播、图文结合滑动切换效果
Apr 16 Javascript
Vue数据驱动模拟实现1
Jan 11 Javascript
js实现漫天星星效果
Jan 19 Javascript
字太多用...代替的方法(两种)
Mar 15 Javascript
select自定义小三角样式代码(实用总结)
Aug 18 #Javascript
js使用highlight.js高亮你的代码
Aug 18 #Javascript
二维码图片生成器QRCode.js简单介绍
Aug 18 #Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
Aug 18 #Javascript
JavaScript实现移动端页面按手机屏幕分辨率自动缩放的最强代码
Aug 18 #Javascript
移动设备手势事件库Touch.js使用详解
Aug 18 #Javascript
JavaScript你不知道的一些数组方法
Aug 18 #Javascript
You might like
解决文件名解压后乱码的问题 将文件名进行转码的代码
2012/01/10 PHP
Codeigniter+PHPExcel实现导出数据到Excel文件
2014/06/12 PHP
变量在 PHP7 内部的实现(二)
2015/12/21 PHP
javascript fullscreen全屏实现代码
2009/04/09 Javascript
jcarousellite.js 基于Jquery的图片无缝滚动插件
2010/12/30 Javascript
php的文件上传入门教程(实例讲解)
2014/04/10 Javascript
jQuery制作简单柱状图实例
2015/01/28 Javascript
浅谈javascript中onbeforeunload与onunload事件
2015/12/10 Javascript
JavaScript引用类型和基本类型详解
2016/01/06 Javascript
JS针对浏览器窗口关闭事件的监听方法集锦
2016/06/24 Javascript
bootstrap table 表格中增加下拉菜单末行出现滚动条的快速解决方法
2017/01/05 Javascript
JavaScript实现兼容IE6的收起折叠与展开效果实例
2017/09/20 Javascript
详解Angular调试技巧之报错404(not found)
2018/01/31 Javascript
Vue 父子组件数据传递的四种方式( inheritAttrs + $attrs + $listeners)
2018/05/04 Javascript
解决layer.confirm快速点击会重复触发事件的问题
2019/09/23 Javascript
javascript实现滚动条效果
2020/03/24 Javascript
详解三种方式在React中解决绑定this的作用域问题并传参
2020/08/18 Javascript
在MAC上搭建python数据分析开发环境
2016/01/26 Python
Python编程实现控制cmd命令行显示颜色的方法示例
2017/08/14 Python
基于python select.select模块通信的实例讲解
2017/09/21 Python
python实现电脑自动关机
2018/06/20 Python
python爬虫URL重试机制的实现方法(python2.7以及python3.5)
2018/12/18 Python
详解Python读取yaml文件多层菜单
2019/03/23 Python
python 批量解压压缩文件的实例代码
2019/06/27 Python
详解Python3 pickle模块用法
2019/09/16 Python
Pytorch 使用不同版本的cuda的方法步骤
2020/04/02 Python
html5 postMessage解决跨域、跨窗口消息传递方案
2016/12/20 HTML / CSS
找到不普通的东西:Bonanza
2016/10/20 全球购物
《在山的那边》教学反思
2014/02/23 职场文书
护士感人事迹
2014/05/01 职场文书
优秀毕业生的求职信
2014/07/21 职场文书
学校通报表扬范文
2015/05/04 职场文书
党风廉政承诺书2016
2016/03/25 职场文书
apache基于端口创建虚拟主机的示例
2021/04/24 Servers
vue实现可拖拽的dialog弹框
2021/05/13 Vue.js
使用Python获取字典键对应值的方法
2022/04/26 Python