导航

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

侧边栏
热门文章
1博文
时隔两年的《冒险岛Online》线下聚会——Super Maple Party 2025
热度
1173
2页面
程序员老黄历&求签
热度
377
3推文
《Happy Sugar Life》的作者签名复制原画到啦!
热度
369
4推文
让AI生成了一张 Happy Sugar Life 的手办图。 效果出乎意料的好,太尊啦!
热度
226
5页面
足迹
热度
143
6页面
游戏
热度
117
7推文
由于韩国Nexon的一系列迷惑操作,导致全球的R区玩家买单。 在2024年12月11日的日服更新中,日服R区开始了第一轮普区化改造。 一夜间,软棉棉的资产被清0,所有装备被上了锁,所有值钱的道具被清空,5转核心和碎片被清除。 相对应的,开放了交易和拍卖功能,以及间接的日元换游戏币的功能。 另外,第一轮普区化还没有删除R区的伤害buff技能,以及6转的升级材料也还保留。
热度
104
8页面
友链
热度
104
9页面
追番
热度
91
10推文
将博客端的Nuxt版本从3升级到了4。 按照官方的升级教程走非常顺利的就升到了4。唯一的问题就是会报弃用警告,不知道是Nuxt UI导致的还是本体导致。跑了跑,发现没什么问题就上到维基萌上了。 中途想把Nuxt UI也从2升级4。但是一直没成功,按照官方教程走,结果Tailwindcss虽然安装了但是一直没生效,就退回到2的最新版了。 但是2的最新版也有点问题,就是编译时间加倍,这个问题4也有。如果继续用老版本的UI吧,提示不兼容当前Nuxt,进退两难。
热度
90
最新评论
Zrzzz
2025-11-14 19:57
@广树:我定语缺失了 大佬严谨
广树管理员
2025-11-14 08:22
@Spike:是这样的,不过最大的意义还是可以前进后退时保持浏览图片的状态。
广树管理员
2025-11-14 08:21
@Zrzzz:是我推的结婚照,圆满了
Spike
2025-11-13 23:38
em...我觉得要克制地使用,一旦触发,如果读者网络比较慢,可能就得在那等好一阵子,体验不太好。(虽然他也可以自己关闭啦)
Zrzzz
2025-11-13 23:25
家里挂着结婚照,某种意义上圆满了
攻略中
全民高尔夫环球之旅
暂无评分
Steam全民高尔夫环球之旅
2025年9月07日 12时 ~ 攻略中
已累计游玩2个月8天
《全民高尔夫 环球之旅》除了能体验到与过去作品相同的乐趣、爽快与深奥之外, 还有着以精美图像设计而成、个性丰富的角色们与以世界各地为主题的球场, 以及能让所有人热血沸腾的全新模式等等,是本系列的全新力作!
ToHeart
暂无评分
SteamToHeart
2025年6月26日 19时 ~ 攻略中
已累计游玩4个月21天
AQUAPLUS推出的“温暖人心的校园恋爱游戏”将以高清全3D形式焕新归来!
fault - StP - LIGHTKRAVTE
暂无评分
Steamfault - StP - LIGHTKRAVTE
2025年5月29日 20时 ~ 攻略中
已累计游玩5个月19天
全球累计销量超过50万份的“fault”系列最新作!故事的舞台是一个融合了奇幻与科幻的超前世界——卢森海德王国。本作讲述了生活在此地的一个平凡又平庸的究极普通市民——名为果子的少年的故事。
PSN奖杯卡

PSN奖杯卡

归档
赞助商广告

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

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

早在去年十月,我写过一篇题为【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;
}


donate.png


telegram banner (1).png


1210 x 50(蓝底).png

cloudcone