导航

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

侧边栏
热门文章
1推文
今天有点事路过了有楽町就顺便逛了逛《别当欧尼酱了!》1周年纪念商店。本以为是一个普通的POP UP SHOP,结果发现做的还挺大的! 一圈逛下来其实最想要的是猫豆腐老师的玩偶,但是似乎是非卖品😅。 拍了一些照片,顺便测试一下博客最近更新的图文灯箱展示模式。
热度
4563
2博文
探访原作中被大海淹没的城市:动画《亚托莉 -我挚爱的时光-》圣地巡礼之铫子
热度
3003
3页面
程序员老黄历&求签
热度
728
4推文
看完《孤独摇滚》的总集篇剧场版后篇了。 虽然删减在所难免,但是个人觉得制作组还是在尽量在保留精华。 ED歌曲《Re:Re:》把我惊艳到了,非常熟悉的旋律当时硬是想不起来,回到家查了一下原来第一次听到是在《只有我不在的街道》。 https://www.bilibili.com/video/BV1M5ejeMErR
热度
533
5博文
探访《蜡笔小新》老家——春日部游记
热度
377
6推文
尝试让新出的GPT 5.6制作了一个图片压缩的小工具。 使用了electron构筑。 可以在windows电脑上直接打开使用。 恭喜你的电脑又多了一个chrome。 地址:https://github.com/eeg1412/blog-image-compression-tool
热度
117
7博文
《上伊那牡丹,酒醉身姿似百合花般》圣地巡礼之杂司谷旧传教士馆
热度
117
8博文
游览世界文化遗产 《寒蝉鸣泣之时》圣地巡礼——白川乡 下篇
热度
117
9博文
3DS模拟器简体中文字库 | Citra3ds字库 | 符文工房4乱码解决字库 | shared_font.bin
热度
117
10博文
Live2d模型合集分享 | FaceRig | 海王星系列 | 初音未来 | 雷姆 | 凉风青叶 | 尼禄 | 薇尔莉特 | 小埋 | 樱花庄 | 玉藻前
热度
117
最新评论
广树管理员
2026-07-15 18:51
@MqyGalaxy:谢谢夸奖,都是博客用下来的经验
广树管理员
2026-07-15 18:50
@石樱灯笼:因为缺少我想要的功能
MqyGalaxy
2026-07-15 13:53
好用!一下子就能批量给博客的图片瘦身了,而且功能挺实用的还支持删除拍摄信息
石樱灯笼
2026-07-14 14:56
为啥不直接用magick
广树管理员
2026-07-11 12:19
@666:因为avif格式到24年才几乎被主流浏览器覆盖。webp的缺点是不支持hdr
攻略中
节奏天国 奇迹之星
暂无评分
NS节奏天国 奇迹之星
2026年7月08日 20时 ~ 攻略中
已累计游玩1周1天
《节奏天国:奇迹之星》是一款轻松又充满创意的音乐节奏游戏,玩家只需跟随音乐节拍按下按键,就能完成跳跃、敲击空罐、制作料理等各种有趣的小挑战。游戏延续了《节奏天国》系列简单易上手、节奏感强、画风幽默的特点,让玩家在一个个短小精巧的关卡中感受音乐与反应力结合的乐趣,适合喜欢节奏游戏和轻松休闲玩法的玩家体验。
告别回忆 -无垢少女- 致最爱的你
暂无评分
Steam告别回忆 -无垢少女- 致最爱的你
2026年6月24日 20时 ~ 攻略中
已累计游玩3周1天
系列作发售至今经历了19年,做为其系列最终作的《告别回忆 ~无垢少女~》(以下略称为本篇)在2018年3月发售。本次的FanDisc和至今的系列作相同,带给玩家在各结局后的短篇故事。请充分享受和各个女主角们在欢乐结局后的幸福时光。
传颂之物:循白之证
暂无评分
PS5传颂之物:循白之证
2026年5月31日 19时 ~ 攻略中
已累计游玩1个月16天
《传颂之物:循白之证》是 AQUAPLUS 开发、Shiravune 发行的《传颂之物》系列动作 RPG。故事发生在大国“大和”恢复和平之后:奥修特尔、御雷、宗近、修娘等人击退敌国阿瓦什兰的侵略,本应迎来安宁,但各地却陆续出现看似毫无关联的怪异现象,并逐渐指向同一个源头;随着三条线索彼此重叠,众人再次踏上旅程,而旅途尽...
亚路塔:狐狸狐途的面包冒险
暂无评分
Steam亚路塔:狐狸狐途的面包冒险
2026年2月12日 19时 ~ 攻略中
已累计游玩5个月4天
一场席卷全世界的面包革命现在开始! 主人公查雅阴错阳差之下,开始了在荒废的岛屿上经营面包店的生活。 出外冒险搜集食材、研发崭新面包配方、遇见性格各异的伙伴们,建造更丰富的设施,用面包香气使无人问津的广场再次热闹起来!
ToHeart
暂无评分
SteamToHeart
2025年6月26日 19时 ~ 攻略中
已累计游玩1年
AQUAPLUS推出的“温暖人心的校园恋爱游戏”将以高清全3D形式焕新归来!
fault - StP - LIGHTKRAVTE
暂无评分
Steamfault - StP - LIGHTKRAVTE
2025年5月29日 20时 ~ 攻略中
已累计游玩1年1个月
全球累计销量超过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;
}


收款.png


file_000000008460720687635c008459256b.png