导航

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

侧边栏
热门文章
1推文
今天有点事路过了有楽町就顺便逛了逛《别当欧尼酱了!》1周年纪念商店。本以为是一个普通的POP UP SHOP,结果发现做的还挺大的! 一圈逛下来其实最想要的是猫豆腐老师的玩偶,但是似乎是非卖品😅。 拍了一些照片,顺便测试一下博客最近更新的图文灯箱展示模式。
热度
5148
2博文
探访原作中被大海淹没的城市:动画《亚托莉 -我挚爱的时光-》圣地巡礼之铫子
热度
3315
3页面
程序员老黄历&求签
热度
741
4推文
看完《孤独摇滚》的总集篇剧场版后篇了。 虽然删减在所难免,但是个人觉得制作组还是在尽量在保留精华。 ED歌曲《Re:Re:》把我惊艳到了,非常熟悉的旋律当时硬是想不起来,回到家查了一下原来第一次听到是在《只有我不在的街道》。 https://www.bilibili.com/video/BV1M5ejeMErR
热度
468
5博文
探访《蜡笔小新》老家——春日部游记
热度
442
6博文
3DS模拟器简体中文字库 | Citra3ds字库 | 符文工房4乱码解决字库 | shared_font.bin
热度
130
7推文
尝试让新出的GPT 5.6制作了一个图片压缩的小工具。 使用了electron构筑。 可以在windows电脑上直接打开使用。 恭喜你的电脑又多了一个chrome。 地址:https://github.com/eeg1412/blog-image-compression-tool
热度
117
8博文
《上伊那牡丹,酒醉身姿似百合花般》圣地巡礼之杂司谷旧传教士馆
热度
117
9博文
游览世界文化遗产 《寒蝉鸣泣之时》圣地巡礼——白川乡 下篇
热度
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小游戏】制作一个简单的赛车游戏

2017-10-18 15:28:38
JavaScript

基于上次的丢鱼雷小游戏的代码。这次删除了投弹相关的代码,并在原先代码的基础上制作了图片,让游戏更像是FC红白机时代的游戏。

因为有了图片,所以就要注意图片必须要加载完才能运行canvas绘制。这里就用到了读取相关的代码:


var imgSrc = [
	"img/bg.png",
	"img/mycar.png",
	"img/emcar.png",
	"img/boom.png"
]
var images = [];

function loading(){
	var imgLength = imgSrc.length;
	var loadingNum = 0;
	for(var i=0;i<imgLength;i++){
		images[i] = new Image();
		images[i].src = imgSrc[i];
		images[i].onload = function(){
			loadingNum++;
			if(loadingNum===imgLength){
				loadingOK();
			}
		}
	}
}

function loadingOK(){
	ctx.clearRect(0,0,canvas.width,canvas.height);//清空画布
	ctx.font = String(14*devicePixelRatio)+"px Courier New";//设定字体
	ctx.fillStyle = "#333";//设定字颜色
	ctx.textAlign="center";//设定字居中
	ctx.fillText("点击开始游戏", Math.round(ctxW*blockSize/2), 50*devicePixelRatio);
	canvas.onclick=function(event){
		init();
	};
}
另外,为了适应手机端,所以这次还加入了虚拟按键。以及在手机上运行后才发现还要注意PPI的问题。


众所周知,手机的PPI是比手机要高,也就是说精度要比电脑高,所以就会导致了一样是20px的图片,在手机上模糊,电脑上是正常的。所以在制图的时候需要制作2倍以上的精度,也就是说20x20的图片做成40x40来应付手机的高PPI,并通过如下代码实现。


var devicePixelRatio = window.devicePixelRatio || 1;//获取屏幕的ppi
var blockSize = 20*devicePixelRatio;//每格像素
var ctxW = 12;//宽多少格
var ctxH = 6;//高多少格
canvas.width=ctxW*blockSize;//设定画布宽度,就是格子*每格像素
canvas.height=ctxH*blockSize;//设定画布高度,就是格子*每格像素
canvas.style.width = ctxW*blockSize/devicePixelRatio + "px";
canvas.style.height = ctxH*blockSize/devicePixelRatio +"px";
注意点大概就这么多,下面直接丢全部的代码。


HTML:


<div class="canvasbox">
    <canvas id="myCanvas" class="canvas"></canvas>
    <div>分数:<span id="score"></span></div>
    <div>HP:<span id="HP"></span></div>
</div>
<p style="margin-top:10px;">操作说明:方向键可以控制方向。或点击下方按钮。</p>
<div class="btnbox">
	<button type="button" class="up btn" id="up">上</button>
    <button type="button" class="down btn" id="down">下</button>
    <button type="button" class="left btn" id="left">左</button>
    <button type="button" class="right btn" id="right">右</button>
</div>


CSS:


.canvas{
	border:1px solid #333;
	cursor:pointer;
	display:block;
	margin:0 auto;
	margin-bottom:10px;
}
.btnbox{
	position:relative;
	z-index:1;
	width:100%;
	height:160px;
	margin-top:10px;
}
.btn{
	width:75px;
	height:75px;
	border:1px solid #525252;
	background:none;
	position:absolute;
	z-index:1;
}
.btn.up{
	top:0;
	left:50%;
	margin-left:-37px;
}
.btn.down{
	top:85px;
	left:50%;
	margin-left:-37px;
}
.btn.left{
	top:42px;
	left:0;
}
.btn.right{
	top:42px;
	right:0;
}


JS:


var canvas=document.getElementById('myCanvas');//获取画布
var scoreSpan = document.getElementById('score');//获取得分span
var HPSpan = document.getElementById('HP');//获取HP的span
var ctx=canvas.getContext('2d');//用于在画布上绘图的环境
var devicePixelRatio = window.devicePixelRatio || 1;//获取屏幕的ppi
var blockSize = 20*devicePixelRatio;//每格像素
var ctxW = 12;//宽多少格
var ctxH = 6;//高多少格
canvas.width=ctxW*blockSize;//设定画布宽度,就是格子*每格像素
canvas.height=ctxH*blockSize;//设定画布高度,就是格子*每格像素
canvas.style.width = ctxW*blockSize/devicePixelRatio + "px";
canvas.style.height = ctxH*blockSize/devicePixelRatio +"px";

var imgSrc = [
	"img/bg.png",
	"img/mycar.png",
	"img/emcar.png",
	"img/boom.png"
]
var images = [];

ctx.font = String(14*devicePixelRatio)+"px Courier New";//设定字体
ctx.fillStyle = "#333";//设定字颜色
ctx.textAlign="center";//设定字居中
ctx.fillText("读取中...", Math.round(ctxW*blockSize/2), 50*devicePixelRatio);
var emt = null;//用于存放定时器,用于更新敌车坐标
var creatEm = null;//用于存放定时器,用于增加敌车
var flush = null;//用于存放定时器,用于刷新画布
var addscoreT = null;

var myShipY=0;//我方的Y初始位置
var myShipX=0;//我方X初始位置
var emShip = [];//存放敌方数据
var boom = [];//存放爆炸效果数据
var hard = 0;//难度(刷新速度,越小刷新越快单位毫秒)
var score = 0;//得分
var HP = 0;//HP

var BGx = 0;

function loading(){
	var imgLength = imgSrc.length;
	var loadingNum = 0;
	for(var i=0;i<imgLength;i++){
		images[i] = new Image();
		images[i].src = imgSrc[i];
		images[i].onload = function(){
			loadingNum++;
			if(loadingNum===imgLength){
				loadingOK();
			}
		}
	}
}

function loadingOK(){
	ctx.clearRect(0,0,canvas.width,canvas.height);//清空画布
	ctx.font = String(14*devicePixelRatio)+"px Courier New";//设定字体
	ctx.fillStyle = "#333";//设定字颜色
	ctx.textAlign="center";//设定字居中
	ctx.fillText("点击开始游戏", Math.round(ctxW*blockSize/2), 50*devicePixelRatio);
	canvas.onclick=function(event){
		init();
	};
}

function init(){//初始化

	myShipY=1;//设定我方的Y初始位置
	myShipX=1;//设定我方的X初始位置
	emShip = [];//清空敌车数据
	boom = [];//清空爆炸效果数据
	hard = 3000;//初始化难度
	score = 0;//初始化分数
	HP = 3;//初始化HP
	
	ctx.clearRect(0,0,canvas.width,canvas.height);//清空画布
	
	creatEmShip();//创建敌车
	paintMyShip();//绘制我方
	paintEmship();//绘制敌车
	scoreSpan.innerHTML = String(score);//初始化计分板
	HPSpan.innerHTML = String(HP);//初始化HP剩余量
	
	clearInterval(emt);//清除更新敌车坐标定时器器
	clearInterval(creatEm);//清除创建敌车定时器
	clearInterval(flush);//清除刷新画布定时器
	clearInterval(addscoreT);
	
	emt = setInterval(function(){//每400毫秒敌车向左移动一次
		emShipMove();
	},140);
	
	creatEm = setInterval(function(){//根据难度创建敌车
		creatEmShip();
	},hard);
	
	flush = setInterval(function(){//100毫秒刷新画布
		ctx.clearRect(0,0,canvas.width,canvas.height);//清空画布
		paintBG();
		paintEmship();//绘制敌车
		paintMyShip();//绘制我方
		boomEmship();//判断是否炸中敌车
	},20);
	
	addscoreT = setInterval(function(){
		addScore();
	},3000);
	
}
function paintBG(){
	ctx.drawImage(images[0],BGx,0,canvas.width*2,canvas.height);
	paintBGMove();
}
function paintBGMove(){
	BGx = BGx-5*devicePixelRatio;
	if(BGx <= -canvas.width){
		BGx = 0;
	}
}
function paintMyShip(){//绘制我方
	var shipX = (myShipX-1)*blockSize;//设定我方船只的X坐标,因为左边是从(0,0)开始,格子定为1开始所以-1
	var shipY = (myShipY-1)*blockSize;//设定我方船只的X坐标,因为左边是从(0,0)开始,格子定为1开始所以-1
	ctx.drawImage(images[1],shipX,shipY,blockSize,blockSize);
}
function creatEmShip(){//随机出现敌车
	var Y = Math.floor(Math.random()*(ctxH) + 1);//高度一共有6个格子,出去丢雷的格子和我方的格子就是6-2,然后就随机从1-4中生成随机数
	emShip.push([blockSize*ctxW,Y,false]);//给敌车数据添加敌车坐标
}
function paintEmship(){//绘制敌车
	for(var i=0;i<emShip.length;i++){//循环敌车数据
		var emShipX = emShip[i][0];//保存敌车X坐标
		var emShipY = (emShip[i][1]-1)*blockSize;//保存敌车Y坐标,因为Y是1-4的所在格子,所以要*每格像素来计算出像素坐标
		if(emShip[i][2]){
			ctx.drawImage(images[3],emShipX,emShipY,blockSize,blockSize);
		}else{
			ctx.drawImage(images[2],emShipX,emShipY,blockSize,blockSize);
		}
	}
}
function emShipMove(){//移动敌车
	for(var i=0;i<emShip.length;i++){//循环敌车数据
		var X = emShip[i][0]-blockSize;//敌车X坐标向左移动一个格子
		emShip[i][0] = X;//重新写入X坐标
		
	}
	removeEmShip();//清除多余的敌车(画布外)
}
function removeEmShip(){//超出范围删除敌车并且HP-1
	for(var i=0;i<emShip.length;i++){//循环敌车数据
		var X = emShip[i][0];//储存敌车X坐标
		if(X<0){//如果已经在画布外
			emShip.splice(i,1);//删除敌车
		}
	}
}
function boomEmship(){//判定炸了
		var boom_X = (myShipX-1)*blockSize;//保存X坐标
		var boom_Y = (myShipY-1)*blockSize;//保存Y坐标
		for(var j=0;j<emShip.length;j++){//循环敌车数据
			var emShip_X = emShip[j][0];//保存敌车X坐标
			var emShip_Y = emShip[j][1]-1;//保存敌车Y坐标
			var emShipIsBoom = emShip[j][2];
			if(boom_X===emShip_X){//判断是否X坐标相同
				if(boom_Y===emShip_Y*blockSize){//如果Y坐标也相同
					if(!emShipIsBoom){
						minusHP();//扣血
						emShip[j][2] = true;
					}
				}
			}
		}
}
function gameOver(){//游戏结束
	ctx.clearRect(0,0,canvas.width,canvas.height);//清空画布
	clearInterval(emt);
	clearInterval(creatEm);
	clearInterval(flush);//清空所有定时器
	clearInterval(addscoreT);
	ctx.font = String(14*devicePixelRatio)+"px Courier New";//设定字体
	ctx.fillStyle = "#333";//设定字颜色
	ctx.textAlign="center";//设定字居中
	ctx.fillText("游戏结束,你得了"+score+"分", Math.round(ctxW*blockSize/2), 50*devicePixelRatio);
	//显示游戏结束,你得了x分在画布中间
}
function addScore(){//加分
	score ++;//加分
	scoreSpan.innerHTML = String(score);//更新计分板
	hard = hard-100;//难度增加20
	if(hard<=300){
		hard=300;//如果难度已经是800了则保持800
	}
	creatEmShip();//立刻创建一个敌车
	clearInterval(creatEm);
	creatEm = setInterval(function(){//根据难度创建敌车
		creatEmShip();
	},hard);
}
function minusHP(){//减HP
	HP --;//减HP
	if(HP<=0){
		HPSpan.innerHTML = String(HP);//更新HP剩余量
		setTimeout("gameOver()",0);//如果HP为0则游戏结束
	}else{
		HPSpan.innerHTML = String(HP);//更新HP剩余量
	}
}

document.onkeydown=function(event){//绑定键盘事件
	var e = event || window.event || arguments.callee.caller.arguments[0];
	if(e && e.keyCode==37){//左
		if(myShipX===1){//防止我方移动到画布外面
			return
		}
		myShipX= myShipX-1;
	}
	if(e && e.keyCode==39){//右
		if(myShipX===ctxW){//防止我方移动到画布外面
			return
		}
		myShipX= myShipX+1;
	}
	
	if(e && e.keyCode==38){//上
		if(myShipY===1){//防止我方移动到画布外面
			return
		}
		myShipY= myShipY-1;
	}
	
	if(e && e.keyCode==40){//下
		if(myShipY===ctxH){//防止我方移动到画布外面
			return
		}
		myShipY= myShipY+1;
	}
	
	
}
document.getElementById('up').onclick=function(event){
	if(myShipY===1){//防止我方移动到画布外面
		return
	}
	myShipY= myShipY-1;
}
document.getElementById('down').onclick=function(event){
	if(myShipY===ctxH){//防止我方移动到画布外面
		return
	}
	myShipY= myShipY+1;
}
document.getElementById('left').onclick=function(event){
	if(myShipX===1){//防止我方移动到画布外面
		return
	}
	myShipX= myShipX-1;
}
document.getElementById('right').onclick=function(event){
	if(myShipX===ctxW){//防止我方移动到画布外面
		return
	}
	myShipX= myShipX+1;
}

loading();


手机可以点击:预览DEMO

收款.png


file_000000008460720687635c008459256b.png