正在召唤神秘力量
导航

萌即是正义!时不时分享一些ACG活动记录与有趣代码的小站!

【H5小游戏】利用pixi.js重写了赛车小游戏

作者:广树·时间:2018-09-16 18:16:46·分类:JavaScript/jQuery/Vue

早在去年十月,我写过一篇题为【H5小游戏】制作一个简单的赛车游戏》的文章。基于最基本的canvas功能,而且是以格子的形式去刷新游戏界面,游戏原理类似上世纪的game&watch。

然后最近学了些pixi.js的东西(相关文章可以从2D WebGL renderer Pixi.js v4 入门【第一回】开始看),于是就把游戏换成了基于pixi.js的游戏。

游戏质量也从game&watch模式提升到了逐帧模式了。不过总体还是很垃圾,毕竟是四个小时瞎搞出来的。

那么废话不多说,先上预览效果。(感觉游戏难度更难了……)


<点击游玩游戏>


然后是代码部分,原理基本上都注释了。这次代码基于pixi.js v4.8.2。相关内容也都是之前pixi入门中提到过的内容。

//别名
let Application = PIXI.Application,
    Container = PIXI.Container,
    loader = PIXI.loader,
    resources = PIXI.loader.resources,
    Graphics = PIXI.Graphics,
    TextureCache = PIXI.utils.TextureCache,
    Sprite = PIXI.Sprite,
    Text = PIXI.Text,
    TextStyle = PIXI.TextStyle;

//创建一个pixi应用
let app = new Application({ 
    width: 240, 
    height: 120,                       
    antialiasing: true, 
    transparent: false, 
    resolution: 2
  }
);

//将pixi插入文档
document.body.appendChild(app.view);
//加载素材图片
loader
  .add("img/bg.png")
  .add("img/boom.png")
  .add("img/emcar.png")
  .add("img/mycar.png")
  .load(setup);
//设置要用到的变量
let state,gameScene,gameOverScene,message,bg,mycarSprite,blobs,numberOfBlobs,score,hp;
function setup() {
  numberOfBlobs = 2;//初始化最大敌车数量
  score = 0;//初始化分数
  hp = 5;//初始化HP
  document.getElementById('hp').innerHTML = String(hp);//将HP写入元素
  //设置游戏场景容器
  gameScene = new Container();
  app.stage.addChild(gameScene);
  //设置背景
  bg = new Sprite(resources["img/bg.png"].texture);
  bg.width = 480;
  bg.height = 120;
  gameScene.addChild(bg);

  //设置自己的车
  mycarSprite=new Sprite(resources["img/mycar.png"].texture);
  mycarSprite.position.set(2,5);//自己的车的初始位置
  mycarSprite.width = 18;//自己的车宽度
  mycarSprite.height = 11;//高度
  mycarSprite.vy = 0;//y轴加速度
  mycarSprite.vx = 0;//x轴加速度
  mycarSprite.invl = 0;//初始化无敌时间
  gameScene.addChild(mycarSprite);

  //一个存地方车辆的数组
  blobs = [];

  //创建一个游戏结束的场景
  gameOverScene = new Container();
  app.stage.addChild(gameOverScene);

  //先让游戏结束场景隐藏
  gameOverScene.visible = false;

  //设置一个默认字体
  let style = new TextStyle({
    "wordWrap": true,
    "align": "center",
    "fill": "white",
    "fontSize": 14
  });
  message = new Text("", style);
  message.y = app.stage.height / 2 - 32;
  message.x = app.stage.width / 4;
  //字体中心点用于居中
  message.anchor.set(0.5,0.5)
  gameOverScene.addChild(message);

  //设置按键代码
  let left = keyboard(37),
      up = keyboard(38),
      right = keyboard(39),
      down = keyboard(40);

  //左按下去之后
  left.press = function() {

    //改变自己车子的加速度
    mycarSprite.vx = -5;
    mycarSprite.vy = 0;
  };

  //松开左键
  left.release = function() {

    //防止键位冲突
    if (!right.isDown && mycarSprite.vy === 0) {
      mycarSprite.vx = 0;
    }
  };

  //Up
  up.press = function() {
    mycarSprite.vy = -5;
    mycarSprite.vx = 0;
  };
  up.release = function() {
    if (!down.isDown && mycarSprite.vx === 0) {
      mycarSprite.vy = 0;
    }
  };

  //Right
  right.press = function() {
    mycarSprite.vx = 5;
    mycarSprite.vy = 0;
  };
  right.release = function() {
    if (!left.isDown && mycarSprite.vy === 0) {
      mycarSprite.vx = 0;
    }
  };

  //Down
  down.press = function() {
    mycarSprite.vy = 5;
    mycarSprite.vx = 0;
  };
  down.release = function() {
    if (!up.isDown && mycarSprite.vx === 0) {
      mycarSprite.vy = 0;
    }
  };
  state = play;
  app.ticker.add(delta => gameLoop(delta));
}
function creatEMCar(){
  //设置敌人
  //根据最大敌车数创建车子
  for (let i = 0; i < numberOfBlobs-blobs.length; i++) {

    //创建敌车
    let blob = new Sprite(resources["img/emcar.png"].texture);
    blob.width = 18;
    blob.height = 11;
    let EMCarIsHit = true;
    while(EMCarIsHit){
      //随机车子x坐标在画布外
      let x = randomInt(250, 500);

      //随机y坐标在画布范围内
      let y = randomInt(2, app.stage.height - 2 - blob.height);

      //设置车辆位置
      blob.x = x;
      blob.y = y;

      //检测车辆位置是否有重叠
      EMCarIsHit = checkEMCarPositionHit(blob);
    }

    //将车子加入数组
    blobs.push(blob);

    //渲染
    gameScene.addChild(blob);
  }
}
//检查生成的地方车辆是否有重叠
function checkEMCarPositionHit(blob){
  for(var i=0;i<blobs.length;i++){
    if(hitTestRectangle(blob,blobs[i])){
      return true;
      break;
    }
  }
}
function gameLoop(delta){
  //更新游戏场景:
  state(delta);
}
function play(delta) {
  //背景移动
  bg.x -= 10;
  if(bg.x<-230){
    bg.x = 0;
  }
  //移动赛车
  mycarSprite.x += mycarSprite.vx;
  mycarSprite.y += mycarSprite.vy;
  if(mycarSprite.invl>0){
    mycarSprite.invl --;
  }
  //判断车辆是否超出画布
  contain(mycarSprite, {x: 0, y: 2, width: 240, height: 118});
  //设置敌人
  creatEMCar();
  let explorerHit = false;
  //移动敌人
  blobs.forEach(function(blob) {

    //移动地方车辆
    blob.x -= 5;

    //如果和我方车辆发生碰撞
    if(hitTestRectangle(mycarSprite, blob)) {
      explorerHit = true;
    }
  });
  //去除已经行驶到画布外的敌军车辆
  removeEmCar();
  //加分
  score += 1;
  document.getElementById('score').innerHTML = String(score);
  //如果分数大于2000则每1000分场景车辆+1,最多八辆
  if(score>2000){
    numberOfBlobs = Math.floor(score/1000);
    if(numberOfBlobs>8){
      numberOfBlobs = 8;
    }
  }
  //如果发生碰撞且在无敌时间之外
  if(explorerHit&&mycarSprite.invl===0) {

    //车子图片变更为爆炸
    mycarSprite.texture = resources["img/boom.png"].texture;
    //减血
    hp --;
    document.getElementById('hp').innerHTML = String(hp);
    //判断是否血量归零
    if(hp<1){
      //抛出游戏结束
      state = end;
      message.text = "你的车炸了!刷新重来";
    }
    mycarSprite.invl = 30//设置无敌时间

  }else if(mycarSprite.invl<=0){//如果不在无敌时间内
    //车辆贴图变回去
    mycarSprite.texture = resources["img/mycar.png"].texture;
  } else {//其他情况
    mycarSprite.texture = resources["img/mycar.png"].texture;
  }
}
//移除已经行驶到画布外围的敌方车辆
function removeEmCar(){
  for(var i=0;i<blobs.length;i++){
    if(blobs[i].x<-50){
      //从数组删除
      blobs.splice(i, 1);
      //从场景删除
      gameScene.removeChildAt(i+2);
    }
  }
}
//按键方法
function keyboard(keyCode) {
  var key = {};
  key.code = keyCode;
  key.isDown = false;
  key.isUp = true;
  key.press = undefined;
  key.release = undefined;

  key.downHandler = function(event) {
    if (event.keyCode === key.code) {
      if (key.isUp && key.press) key.press();
      key.isDown = true;
      key.isUp = false;
    }
    event.preventDefault();
  };

  key.upHandler = function(event) {
    if (event.keyCode === key.code) {
      if (key.isDown && key.release) key.release();
      key.isDown = false;
      key.isUp = true;
    }
    event.preventDefault();
  };

  window.addEventListener(
    "keydown", key.downHandler.bind(key), false
  );
  window.addEventListener(
    "keyup", key.upHandler.bind(key), false
  );
  return key;
}
//判断是否超出画布
function contain(sprite, container) {

  let collision = undefined;

  //Left
  if (sprite.x < container.x) {
    sprite.x = container.x;
    collision = "left";
  }

  //Top
  if (sprite.y < container.y) {
    sprite.y = container.y;
    collision = "top";
  }

  //Right
  if (sprite.x + sprite.width > container.width) {
    sprite.x = container.width - sprite.width;
    collision = "right";
  }

  //Bottom
  if (sprite.y + sprite.height > container.height) {
    sprite.y = container.height - sprite.height;
    collision = "bottom";
  }

  return collision;
}
function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
//碰撞检测
function hitTestRectangle(r1, r2) {

  //设置需要的变量
  let hit, combinedHalfWidths, combinedHalfHeights, vx, vy;

  //初始化是否碰撞
  hit = false;

  //寻找精灵中心点
  r1.centerX = r1.x + r1.width / 2; 
  r1.centerY = r1.y + r1.height / 2; 
  r2.centerX = r2.x + r2.width / 2; 
  r2.centerY = r2.y + r2.height / 2; 

  //计算精灵一半的宽度和高度
  r1.halfWidth = r1.width / 2;
  r1.halfHeight = r1.height / 2;
  r2.halfWidth = r2.width / 2;
  r2.halfHeight = r2.height / 2;

  //计算相互之间的距离
  vx = r1.centerX - r2.centerX;
  vy = r1.centerY - r2.centerY;

  //计算xy重叠的数值
  combinedHalfWidths = r1.halfWidth + r2.halfWidth;
  combinedHalfHeights = r1.halfHeight + r2.halfHeight;

  //检查x轴是否充电
  if (Math.abs(vx) < combinedHalfWidths) {

    //再检查y轴
    if (Math.abs(vy) < combinedHalfHeights) {

      //如果是则判断为碰撞
      hit = true;
    } else {

      //否则为没碰撞
      hit = false;
    }
  } else {

    //x轴没有碰撞
    hit = false;
  }

  //传是否碰撞
  return hit;
};
//游戏结束显示游戏结束场景,隐藏游戏场景
function end() {
  gameScene.visible = false;
  gameOverScene.visible = true;
}


#pixi.js#H5小游戏
侧边栏
最新评论
广树
2024-04-24
@Chise Hachiroku:是大佬!膜拜! 百度这样的模式真的不是作茧自缚吗?信息越来越狭小。
Chise Hachiroku
2024-04-24
2021年7月和2023年4月因为合作项目的原因,曾经做过各个平台SEO优化的各类尝试,没有量化结果的大致数据如下: 1. 谷歌主动发掘新站点的速度不快,一旦提交抓取更新稳定。比较侧重内容时效,不更新但其他网站更新了,排名会掉。 2. 必应希望网站主可以提交链接,主动发掘速度也比较快。比较侧重于引擎认为的内容相关性和网站流量,导致营销号类网站猖獗。DuckDuckGo也有类似趋势,但不需要提交。 3. 谷歌、必应、DDG喜欢域名和关键词对上,比如Girl Cafe Gun和 girlcafegun.wiki (个人做过SEO的老项目)。 4. 百度从2022年开始基本只收录国内站点(备案齐全的)和以前他们抓取过的外国站点,且国外中文内容对排名有显著负优化。 5. 百度会尝试将英文内容翻译成中文并加入关键词,但由于百度优先中文结果,这些结果经常会压住英文内容。 6. Yandex和Petal的抓取比较迷幻,不过因为并不做俄语内容,并没有做过优化。 供参考。自从23年8月开始停止更新合作项目以来,SEO也已经好几个月没有特征优化过了。 感觉有空了自己可以写一篇SEO优化入门的文章(逃)
广树
2024-04-23
@test_user:大佬这个名字取的好神秘😅。丢失了很多回忆挺可惜的,所以记录还是很重要的!
test_user
2024-04-23
我的那个号久远到我的密码都忘记了,过了些年我去申诉因为什么都没绑所以申诉失败了。。。,我是公测时候玩的,没赶上内测,但是我是的喜欢这个游戏,现在还能一闭眼还能想起魔法密林的 BGM。。。,不过我当年没有截图的意识,感觉丢失了很多的回忆。。。。
广树
2024-04-22
@嫖嫖乐:看来大佬资历很深啊!
正在攻略

圣兽之王.jpg

传颂之物

PSN奖杯卡

PSN奖杯卡

赞助商广告