canvas实现太空飞船射击类游戏ASTEROIDS代码

代码语言:html

所属分类:游戏

代码描述:canvas实现太空飞船射击类游戏ASTEROIDS代码,鼠标移动控制飞船,鼠标左键单击射击。

代码标签: canvas 太空 飞船 射击类 游戏 ASTEROIDS 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  


  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
  
  
  
<style>
* {
    padding: 0;
    margin: 0;
}
body {
    background-color: #000000;
    color: #fff;
    font-size: 12px;
    font-family: Arial, Helvetica, sans-serif;
}
canvas {
    cursor: crosshair;
    position: absolute;
}

a {
    color: #fff;
    text-decoration: none;
    cursor: pointer;
}

#menu {
    position: absolute;
    top: 50%;
    margin-top: -45px;
    text-align: center;
    width: 100%;
}

#start {
    font-size: 20px;
    font-weight: bold;
}

#title {
    font-size: 24px;
    margin-bottom: 15px;
}

#message {
    font-size: 14px;
    margin-bottom: 30px;
}

a#tweet {
    color: white;
    font-size: 11px;
    display: inline-block;
    margin-top: 15px;
    padding: 5px 10px;
    background-color: transparent;
    border: 1px solid white;
}

a#tweet:hover {
    color: black;
    background-color: white;
}

#hud {
    position: absolute;
    top: 10px;
    left: 10px;
    list-style: none;
}

#menu, #hud {
    -moz-user-select: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
}

#hud li.score {
    width: 120px;
    overflow: hidden;
}

#hud li {
    float: left;
    margin-right: 20px;
}
</style>


  
  
</head>

<body >
  <div id='container'>
    <canvas id='c'></canvas>
    <ul id='hud'>
        <li class='score'>SCORE: <span id='score'>0</span></li>
        <li>WEPON: <span id='wepon'>NORMAL BEAM</span></li>
    </ul>
    <div id='menu'>
        <h2 id='title'></h2>
        <div id='message'></div>
        <a href='#' id='start'>START</a>
    </div>
</div>


  
      <script  >
// Machine translation because I'm not good at English!

//------------------------------------
// CONFIG
//------------------------------------

// デフォルトの武器
// Default weapon
var WEPON_DEFAULT = {
  // 武器の名前
  // Weapon name
  name: 'NORMAL BEAM',
  // 与えるダメージ 0~1
  // Damage dealt 0 ~ 1
  power: 0.3,
  // 弾のスピード
  // Bullet speed
  speed: 3,
  // 弾の長さ
  // Bullet length
  length: 10,
  // 弾の幅
  // Bullet width
  width: 1,
  // 弾の色, 特殊武器の場合アイテムの色に反映される, CSS カラーで指定
  // The color of the bullet, in the case of special weapons, reflected in the color of the item, specified by CSS color
  color: 'white',
  // 連射速度
  // Fire rate
  shootingInterval: 1000 * 0.35,
  // 貫通弾か示す
  // true の場合着弾しても消滅しない
  // explosion を指定した場合はそちらが優先される
  // Indicates whether the bullet
  // If true, will not disappear even if it lands
  // If explosion is specified, it takes precedence.
  through: false,
  // 爆発による着弾後の範囲攻撃
  // 以下のプロパティを持つオブジェクトで指定する
  // { range: 爆発範囲, speed: 爆発の速度 }
  // * 範囲攻撃の威力は武器の基本威力の半分
  // Range attack after landing due to explosion
  // Specify with an object that has the following properties:
  // { range: Explosion range, speed: Explosion speed }
  // * The power of range attacks is half the basic power of weapons
  explosion: false };


// 特殊武器の配列, UFO を撃破するとランダムで出現する
// Special weapon array, Randomly appear when you destroy a UFO
var WEPON_SPECIAL = [
{
  name: 'TINY BEAM',
  power: 0.1,
  speed: 10,
  length: 5,
  width: 1,
  color: 'rgb(131, 224, 8)',
  shootingInterval: 1000 * 0.1,
  through: false,
  explosion: false },

{
  name: 'BLASTER',
  power: 1,
  speed: 3,
  length: 15,
  width: 3,
  color: 'rgb(244, 0, 122)',
  shootingInterval: 1000 * 0.3,
  through: false,
  explosion: false },

{
  name: 'LASER',
  power: 0.2,
  speed: 35,
  length: 200,
  width: 2,
  color: 'rgb(138, 227, 252)',
  shootingInterval: 1000 * 0.6,
  through: true,
  explosion: false },

{
  name: 'EXPLOSION BEAM',
  power: 0.15,
  speed: 15,
  length: 10,
  width: 2,
  color: 'rgb(255, 153, 0)',
  shootingInterval: 1000 * 0.5,
  through: false,
  explosion: {
    range: 100,
    speed: 4.5 } }

/*
,{
 name: 'INSANE BEAM',
 power: 0.035,
 speed: 7.5,
 length: 5,
 color: 'rgb(255, 246, 0)',
 width: 2,
 shootingInterval: 1000 * 0.015,
 through: true,
 explosion: false,
explosion: {
	range: 75,
	speed: 2
}
}//*/];


var ASTEROID_MAX_SIZE = 80; // 小惑星の最大サイズ
var ASTEROID_MIN_SIZE = 5; // 小惑星の最小サイズ
var ASTEROID_MAX_NUM = 75; // 小惑星の最大数
var ASTEROID_SPAWN_TIME = 350; // 小惑星の復活時間
var SHIP_SPEED = 1.5; // 自機のスピード
var UFO_SPEED = 2; // UFO のスピード
var ITEM_SPEED = 0.5; // アイテムのスピード

// UFO の出現率 0~1
var UFO_INCIDENCE = 0.0035;

// 特殊武器の持続時間
var SPECIAL_WEPON_TIME = 1000 * 20;

// 各種スコア
var SCORE = {
  ASTEROID_DAMAGE: 10,
  ASTEROID_DESTROY: 50,
  UFO_DAMAGE: 0,
  UFO_DESTROY: 300 };


//------------------------------------
// CONSTANTS
//------------------------------------

var PI = Math.PI;
var TWO_PI = PI * 2;
var DEG_TO_RAD = PI / 180;
var FPS = 60;

//------------------------------------
// VARS
//------------------------------------

var canvas;
var canvasWidth;
var canvasHeight;
var context;
var mouse;
var isMouseDown = false;
var ship; // Ship
var beams; // Collection of Beam
var asteroids; // Collection of Asteroid
var splinters; // Collection of Splinter
var debris; // Collection of Debri
var ufo; // Ufo
var item; // Item
var asteroidLastSpawn = 0; // 小惑星が最後に復活した時間
var ufoLastSpawn = 0; // UFO が最後に復活した時間
var debriLastSpawn = 0; // 背景の星屑が最後に出現した時間
var fieldRange; // フィールドの範囲
var score = 0; // スコア
var isPlay = false; // ゲームが開始されているか示す
var dom = {
  menu: null,
  title: null,
  message: null,
  tweet: null,
  start: null,
  score: null,
  wepon: null };



//------------------------------------
// INITIALIZE
//------------------------------------

function init() {
  canvas = document.getElementById('c');
  fieldRange = new Range();

  window.addEventListener('resize', resize, false);
  resize();

  mouse = new Point();

  dom.menu = document.getElementById('menu');
  dom.title = document.getElementById('title');
  dom.message = document.getElementById('message');
  dom.start = document.getElementById('start');
  dom.score = document.getElementById('score');
  dom.wepon = document.getElementById('wepon');

  dom.start.addEventListener('click', start, false);
  canvas.addEventListener('mousemove', mouseMove, false);
  canvas.addEventListener('mousedown', mouseDown, false);
  canvas.addEventListener('mouseup', mouseUp, false);
  canvas.addEventListener('click', click, false);

  canvas.addEventListener('touchmove', touchMove, false);
  canvas.addEventListener('touchstart', mouseDown, false);
  canvas.addEventListener('touchend', mouseUp, false);

  debris = new Collection();
  for (var i = 0; i < 30; i++) {
    debris.push(new Debri(randInt(canvasWidth)));
  }

  setInterval(loop, 1000 / FPS);
}


//------------------------------------
// EVENT HANDLERS
//------------------------------------

function resize(e) {
  canvas.width = canvasWidth = fieldRange.right = window.innerWidth;
  canvas.height = canvasHeight = fieldRange.bottom = window.innerHeight;

  context = canvas.getContext('2d');
  context.fillStyle = 'white';
  context.strokeStyle = 'white';
  context.lineWidth = 1;
}

function start(e) {
  play();
  dom.menu.style.display = 'none';
  e.preventDefault();
}

function mouseMove(e) {mouse.set(e.clientX, e.clientY);}
function touchMove(e) {mouse.set(e.touches[0].clientX, e.touches[0].clientY);console.log(e.touches[0].clientX, e.touches[0].clientY);}
function mouseDown(e) {isMouseDown = true;}
function mouseUp(e) {isMouseDown = false;}
function click(e) {if (ship) ship.fire(beams);}


//------------------------------------
// FRAME LOOP
//------------------------------------

function loop() {
  context.clearRect(0, 0, canvasWidth, canvasHeight);

  var now = new Date().getTime();

  // Spawn Debri
  if (now - debriLastSpawn > 300) {
    debris.push(new Debri(canvasWidth));
    debriLastSpawn = now;
  }

  // Debri update
  debris.eachUpdate();

  if (isPlay) {
    // Spawn

    if (now - asteroidLastSpawn > ASTEROID_SPAWN_TIME && asteroids.length < ASTEROID_MAX_NUM) {
      asteroids.push(Asteroid.spawn());
      asteroidLastSpawn = now;
    }

    if (!ufo && !item && Math.random() < UFO_INCIDENCE) {
      ufo = Ufo.spawn();
    }

    // Update

    if (ship) {
      if (isMouseDown) ship.fire(beams);
      ship.update(mouse);
    }

    if (ufo) {
      ufo.update();
      if (ufo.vanished) {
        item = new Item(ufo.x, ufo.y);
        ufo = null;
      } else if (hitDetection(ufo, ship)) {
        gameOver();
      }
    }

    if (item) {
      item.update();
      if (item.vanished) {
        item = null;
      } else if (hitDetection(item, ship)) {
        ship.setSpecialWepon(item.wepon);
        item = null;
      }
    }

    beams.eachUpdate(function (index, beam) {
      for (var i = 0; i < asteroids.length; i++) {
        asteroid = asteroids[i];
        if (hitDetection(beam, asteroid)) {
          score += asteroid.damage(beam.power, splinters);
          beam.notifyHit();
        }
      }

      if (ufo && hitDetection(beam, ufo)) {
        score += ufo.damage(beam.power, splinters);
        beam.notifyHit();
      }
    });

    asteroids.eachUpdate(function (index, asteroid) {
      if (hitDetection(asteroid, ship)) {
        gameOver();
      }
    });

    splinters.eachUpdate();

    // Display

    dom.wepon.innerHTML = ship.currentWepon.name;
    dom.score.innerHTML = score;
  }

  // Draw

  context.beginPath();
  context.strokeStyle = 'rgb(255, 255, 255)';
  context.lineWidth = 1;
  if (ship) ship.draw(context);
  if (ufo) ufo.draw(context);
  if (asteroids) asteroids.eachDraw(context);
  context.stroke();

  // Beam は個別に描画
  if (beams) beams.eachDraw(context);

  if (item) {
    context.beginPath();
    context.strokeStyle = item.wepon.color;
    context.lineWidth = 1;
    item.draw(context);
    context.stroke();
  }

  context.beginPath();
  context.fillStyle = 'rgb(255, 255, 255)';
  if (splinters) splinters.eachDraw(context);
  if (debris) debris.eachDraw(context);
  // Game over
  if (ship && ship.died) {
    ship.update();
    ship.splinter.draw(context);
  }
  context.fill();

}


//------------------------------------
// FUNCTIONS
//------------------------------------

function play() {
  ship = new Ship(canvasWidth / 2, canvasHeight / 2, 8);
  mouse.set(ship.x, ship.y);
  beams = new Collection();
  asteroids = new Collection();
  splinters = new Collection();
  ufo = null;
  item = null;
  score = 0;
  isMouseDown = false;
  isPlay = true;
}

function gameOver() {
  ship.destroy();
  isPlay = false;
  dom.title.innerHTML = 'GAME OVER!';
  dom.message.innerHTML = 'YOUR SCORE ' + score + ' POINTS<br />';
  dom.message.appendChild(tweetLink());
  dom.menu.style.display = 'block';
}

function tweetLink() {
  var exc = score < 1000 ? '...' : score > 3000 ? '!!!' : '!';
  if (!dom.tweet) {
    dom.tweet = document.createElement('a');
    dom.tweet.id = 'tweet';
    dom.tweet.innerHTML = 'TWEET YOUR SCORE';
  }
  dom.tweet.href = 'https://twitter.com/intent/tweet?url=https://codepen.io/akm2/pen/eYYyELr&text=SCORE ' + score + ' PTS' + exc + ' - ASTEROIDS';
  dom.tweet.target = '_blank';
  return dom.tweet;
}

// パスの衝突判定を行う
// 引数に指定するオブジェクトは path プロパティから Path オブジェクトが参照可能であること
// Perform path collision detection
// The object specified in the argument can be referenced from the path property.
function hitDetection(a, b) {
  var ap = a.path,bp = b.path;
  var as, bs; // Segments
  var a1, a2, b1, b2; // Points

  for (i = 0, ilen = ap.segmentNum(); i < ilen; i++) {
    as = ap.segment(i);
    a1 = as[0];
    a2 = as[1];
    for (j = 0, jlen = bp.segmentNum(); j < jlen; j++) {
      bs = bp.segment(j);
      b1 = bs[0];
      b2 = bs[1];
      if (intersection(a1, a2, b1, b2)) return true;
    }
  }

  return false;
}

// hitDetection で使用される直線の交差判定
// 交差しているなら true
// Straight line intersection detection used in hitDetection
// True if they intersect
function intersection(a1, a2, b1, b2) {
  var ax = a2.x - a1.x,ay = a2.y - a1.y;
  var bx = b2.x - b1.x,by = b2.y - b1.y;
  return (ax * (b1.y - a1.y) - ay * (b1.x - a1.x)) * (
  ax * (b2.y - a1.y) - ay * (b2.x - a1.x)) <= 0 &&
  (bx * (a1.y - b1.y) - by * (a1.x - b1.x)) * (
  bx * (a2.y - b1.y) - by * (a2.x - b1.x)) <= 0;
}



//------------------------------------
// UTILS
//------------------------------------

function extend() {
  var target = arguments[0] || {},o,p;

  for (var i = 1, len = arguments.length; i < len; i++) {
    o = arguments[i];

    if (!isObject(o) || isNull(o)) continue;

    for (p in o) {
      target[p] = o[p];
    }
  }

  return target;
};

function randUniform(max, min) {
  if (min === undefined) min = 0;
  return Math.random() * (max - min) + min;
};

function randInt(max, min) {
  if (min === undefined) min = 0;
  return Math.floor(Math.random() * (max - min + 1) + min);
};

isObject = function (value) {
  return typeof value === 'object' && value !== null;
};

isNumber = function (value) {
  return typeof value === 'number';
};

isNumeric = function (value) {
  return !isNaN(value) && isFinite(value);
};

isString = function (value) {
  return typeof value === 'string';
};

isFunction = function (value) {
  return typeof value === 'function';
};

isArray = function (value) {
  return Object.prototype.toString.call(value) === '[object Array]';
};

isNull = function (value) {
  return value === null;
};

isUndefined = function (value) {
  return typeof value === 'undefined';
};


//------------------------------------
// CLASSES
//------------------------------------

/**
 * Collection
 * 
 * @super Array
 */
function Collection() {
  for (var i = 0, len = arguments.length; i < len; i++) {
    this.push(arguments[i]);
  }
}

Collection.prototype = extend([], {
  eachUpdate: function (callback) {
    for (var i = 0, len = this.length, item; i < len; i++) {
      item = this[i];

      if (item.vanished) {
        this.splice(i, 1);
        len--;
        i--;
        continue;
      }
      item.update();

      if (callback) callback.call(this, i, item);
    }
  },

  eachDraw: function (ctx) {
    for (var i = 0, len = this.length; i < len; i++) {
      this[i].draw(ctx);
    }
  } });



/**
 * Path
 * 
 * @super Array
 */
function Path(points, closed) {
  if (isArray(points)) {
    for (var i = 0, len = points.length; i < len; i++) {
      this.push(points[i]);
    }
  }

  this.closed = isUndefined(closed) ? true : closed;
}

Path.prototype = extend([], {
  closed: true,

  segment: function (index) {
    if (index > this.segmentNum()) return null;
    return [
    this[index],
    this[index === this.length - 1 ? 0 : index + 1]];

  },

  segmentNum: function () {
    return this.closed ? this.length : this.length - 1;
  },

  eachSegments: function (callback) {
    for (var i = 0, len = this.segmentNum(); i < len; i++) {
      if (callback.call(this, this.segment(i), i) === false) break;
    }
  },

  eachPoints: function (callback) {
    for (var i = 0, len = this.length; i < len; i++) {
      if (callback.call(this, this[i], i) === false) break;
    }
  },

  draw: function (ctx) {
    this.eachPoints(function (p, i) {
      ctx[i === 0 ? 'moveTo' : 'lineTo'](p.x, p.y);
    });

    if (this.closed && this.length > 2) {
      var p = this[0];
      ctx.lineTo(p.x, p.y);
    }
  } });



/**
 * Point
 */
function Point(x, y) {
  this.set(x, y);
};

Point.interpolate = function (p1, p2, f) {
  var dx = p2.x - p1.x,
  dy = p2.y - p1.y;
  return new Point(p1.x + dx * f, p1.y + dy * f);
};

Point.polar = function (length, angle) {
  return new Point(length * Math.cos(angle), length * Math.sin(angle));
};

Point.prototype = {
  set: function (x, y) {
    if (isObject(x)) {
      y = x.y;
      x = x.x;
    }

    this.x = x || 0;
    this.y = y || 0;

    return this;
  },

  offset: function (x, y) {
    this.x += x || 0;
    this.y += y || 0;

    return this;
  },

  add: function (p) {
    this.x += p.x;
    this.y += p.y;

    return this;
  },

  sub: function (p) {
    this.x -= p.x;
    this.y -= p.y;

    return this;
  },

  scale: function (scale) {
    this.x *= scale;
    this.y *= scale;

    return this;
  },

  length: function () {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  },

  lengthSq: function () {
    return this.x * this.x + this.y * this.y;
  },

  normalize: function (thickness) {
    if (isUndefined(thickness)) thickness = 1;

    var len = Math.sqrt(this.x * this.x + this.y * this.y);
    var nx = 0,ny = 0;

    if (len) {
      nx = this.x / len;
      ny = this.y / len;
    }

    this.x = nx * thickness;
    this.y = ny * thickness;

    return this;
  },

  angle: function () {
    return Math.atan2(this.y, this.x);
  },

  angleTo: function (p) {
    var dx = p.x - this.x,
    dy = p.y - this.y;
    return Math.atan2(dy, dx);
  },

  distanceTo: function (p) {
    var dx = this.x - p.x,
    dy = this.y - p.y;
    return Math.sqrt(dx * dx .........完整代码请登录后点击上方下载按钮下载查看

网友评论0