我正在使用JavaScript制作Chip8模拟器。除了2件事之外,我大部分时间都已完成。
主要的性能和图形问题。循环执行速度不够快,因此屏幕闪烁太慢,使游戏变得不那么顺畅。
碰撞检测。 Chip8通过使用XOR确定白色像素是否被覆盖为黑色来检测碰撞,然后将V [15]设置为1.问题是,我无法弄清楚如何比较像素,两个,我有不知道为什么碰撞检测件的“测试”版本不起作用。 Pong(我的默认游戏)中的块只是相互通过。
我想知道如何解决问题#2,因为这会影响模拟器本身的核心。如何进行有效的碰撞检测功能,如果发生碰撞,将V [15]的值设置为1?
这是代码。
<input type="text" size="40" onkeypress="trackkey(event)"></input><br />
<div id="memory" /><div id="debugscreen" />
<canvas id="screen" width="640" height="320" style="border:1px solid #000000;">
</canvas>
<script>
function trackkey(event) {
var x = event.which || event.keyCode;
var y=0;
// alert(x);
switch(x){
case 49://1, 1
y=1;
break;
case 50://2, 2
y=2;
break;
case 51://3, 3
y=3;
break;
case 52://4, 0xc
y=0xc;
break;
case 113://q, 4
y=4;
break;
case 119://w, 5
y=5;
break;
case 101://e, 6
y=6;
break;
case 114://r, 0xD
y=0xD;
break;
case 97://a, 7
y=7;
break;
case 115://s, 8
y=8;
break;
case 100://d, 9
y=9;
break;
case 102://f, 0xe
y=0xE;
break;
case 122://z, 0xA
y=0xA;
break;
case 120://x, 0
y=0;
break;
case 99://c, 0xB
y=0xB;
break;
case 122://v, 0xF
y=0xF;
break;
default:
y=50;
break;
}
y-=1;
if(y!=49){
pressedKey=y;keyDown=true;
for(var i=0;i<16;i++){
if(i!=y){
key[i]=0;
}else{key[i]=1;}
}
}
console.log(keyDown+" "+pressedKey+" "+key);
}
//specs and variables
var opcode;
var memory=new Array(4096);
var V=new Array(16);
var I;
var pc;
var buffer;
var gfx=new Array(64*32);
var delay_timer;
var sound_timer;
var stack=new Array(16);
var sp;
var key=new Array(16);
var drawFlag;
var text=[
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
];
//game loop
main();
function main(){
//setupGraphics();
//setupInput();
setKeys();
loadGame();
initialize();
drawGraphics();
setInterval(gameLoop, 0.000016);
}
function gameLoop(){
emulateCycle();
if(drawFlag){
drawGraphics();
setKeys();//store key press state (press and release)
}
}
//store key press state
function setKeys(){
}
//initialize
function initialize(){
pc=0x200;
opcode=0;
I=0;
sp=0;
//clear();console.log("clear");
for(var i=0;i<0x10;i++){
V[i]=0;
}
for(var i=0;i<80;i++){
memory[i]=text[i];
}
for(var i=0;i<buffer.length;i++){
memory[i+512]=buffer[i];
}
delay_timer=0;
sound_timer=0;
}
//load program
function loadGame(){
buffer=[106,2,107,12,108,63,109,12,162,234,218,182,220,214,110,0,34,212,102,3,104,2,96,96,240,21,240,7,48,0,18,26,199,23,119,8,105,255,162,240,214,113,162,234,218,182,220,214,96,1,224,161,123,254,96,4,224,161,123,2,96,31,139,2,218,182,141,112,192,10,125,254,64,0,125,2,96,0,96,31,141,2,220,214,162,240,214,113,134,132,135,148,96,63,134,2,97,31,135,18,70,2,18,120,70,63,18,130,71,31,105,255,71,0,105,1,214,113,18,42,104,2,99,1,128,112,128,181,18,138,104,254,99,10,128,112,128,213,63,1,18,162,97,2,128,21,63,1,18,186,128,21,63,1,18,200,128,21,63,1,18,194,96,32,240,24,34,212,142,52,34,212,102,62,51,1,102,3,104,254,51,1,104,2,18,22,121,255,73,254,105,255,18,200,121,1,73,2,105,1,96,4,240,24,118,1,70,64,118,254,18,108,162,242,254,51,242,101,241,41,100,20,101,0,212,85,116,21,242,41,212,85,0,238,128,128,128,128,128,128,128,0,0,0,0,0];
}
//clear display
function clear(){
for(var i=0;i<gfx.length;i++){
gfx[i]=0;
}
drawFlag=true;
}
//draw
function draw(x, y, h){
var coordinates;
coordinates=y*64+x;
for(var j=0;j<h;j++){
for(var i=0;i<8;i++){
// if((coordinates+i+64*j)%64!=0){//console.log(gfx[coordinates+i+64*j]==1&&parseInt(parseInt(memory[(I+j)]).toString(2)[i])==0);
if(gfx[coordinates+i+64*j]==1&&parseInt(parseInt(memory[(I+j)]).toString(2)[i])==0){V[0xF]=1;}else{V[0xF]=0;}
// console.log(V[0xF]);
gfx[coordinates+i+64*j]=parseInt(parseInt(memory[(I+j)]).toString(2)[i]);
// }else{i=8;}
}
}
drawFlag=true;
}
//draw graphics
function drawGraphics(){
V[0xF]=0;
for(var i=0;i<gfx.length;i++){
var c = document.getElementById("screen");
var ctx = c.getContext("2d");
ctx.beginPath();
if(gfx[i]==1){ctx.fillStyle = "white";}else{ctx.fillStyle = "black";}
ctx.fillRect(i%64*10, Math.floor(i/64)*10, 10, 10);
ctx.stroke();
}
// clear();
drawFlag=false;
}
//emulate cycle
function emulateCycle(){
// debugger;
opcode=0;
opcode = memory[pc] << 8 | memory[pc + 1];
switch(opcode&0xF000){
//0x0NNN not needed
case 0x0000: //0x00E0 and 0x00EE
switch(opcode&0x000F){
case 0x0000:
clear();//console.log("clear");//clear display
drawFlag=true;
pc+=2;
break;
case 0x000E:
//return from subroutine
pc=stack[sp-1];
sp=sp-1;
pc+=2;
break;
}
break;
case 0x1000://goto subroutine
pc = opcode & 0x0FFF;
break;
case 0x2000://call subroutine
stack[sp]=pc;
sp++;
pc=opcode & 0x0FFF;
break;
case 0x3000://skip next instruction if Vx==NN
if(V[(opcode>>8)&0x000F].toString()==(opcode&0x00FF).toString()){
pc+=2;
}
pc+=2;
break;
case 0x4000://skip next instruction if Vx!=NN
if(V[(opcode>>8)&0x000F]!=opcode&0x00FF){
pc+=2;
}
pc+=2;
break;
case 0x5000://skip next instruction if Vx==Vy
if(V[(opcode>>8)&0x000F]==V[(opcode>>4)&0x000F]){
pc+=2;
}
pc+=2;
break;
case 0x6000://set Vx to NN
V[(opcode>>8)&0x000F]=opcode&0x00FF;
//alert(V[(opcode>>8)&0x000F]);
pc+=2;
break;
case 0x7000://Vx+=NN
V[(opcode>>8)&0x000F]+=opcode&0x00FF;
pc+=2;
break;
case 0x8000:
switch(opcode&0x000F){
case 0x0000://Vx=Vy
V[(opcode>>8)&0x000F]=V[(opcode>>4)&0x000F];
pc+=2;
break;
case 0x0001://Vx=Vx|Vy
V[(opcode>>8)&0x000F]=V[(opcode>>8)&0x000F]|V[(opcode>>4)&0x000F];
pc+=2;
break;
case 0x0002://Vx=Vx&Vy
V[(opcode>>8)&0x000F]=V[(opcode>>8)&0x000F]&V[(opcode>>4)&0x000F];
pc+=2;
break;
case 0x0003://Vx=Vx^Vy
V[(opcode>>8)&0x000F]=V[(opcode>>8)&0x000F]^V[(opcode>>4)&0x000F];
pc+=2;
break;
case 0x0004://Vx+=Vy
if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8])){
V[0xF] = 1; //carry
}else{
V[0xF] = 0;
V[(opcode & 0x0F00) >> 8] += V[(opcode & 0x00F0) >> 4];
}
pc+=2;
break;
case 0x0005://Vx-=Vy
if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8])){
V[0xF] = 1; //carry
}else{
V[0xF] = 0;
V[(opcode & 0x0F00) >> 8] -= V[(opcode & 0x00F0) >> 4];
}
pc+=2;
break;
case 0x0006://Vx=Vy=Vy>>1
V[0xF]=V[(opcode>>4)&0x000F]&0x000F;
V[(opcode>>4)&0x000F]=V[(opcode>>4)&0x000F]>>1;
V[(opcode>>8)&0x000F]=(opcode>>4)&0x000F;
pc+=2;
break;
case 0x0007://Vx=Vy-Vx
if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8])){
V[0xF] = 1; //carry
}else{
V[0xF] = 0;
V[(opcode & 0x00F0) >> 4]=V[(opcode & 0x0F00) >> 8]-V[(opcode & 0x00F0) >> 4];
}
pc+=2;
break;
case 0x000E://Vx=Vy=Vy<<1
V[0xF]=V[(opcode & 0x0F00) >> 8] >> 7;
V[(opcode>>4)&0x000F]=V[(opcode>>4)&0x000F]<<1;
V[(opcode>>8)&0x000F]=V[(opcode>>4)&0x000F];
pc+=2;
break;
}
break;
case 0x9000://if(Vx!=Vy){skip}
if(V[(opcode>>8)&0x000F]!=V[(opcode>>4)&0x000F]){
pc+=2;
}
pc+=2;
case 0xA000://I=NNN
I=opcode&0x0FFF;
pc+=2;
break;
case 0xB000://I=NNN+V0
pc=(opcode&0x0FFF)+V[0];
break;
case 0xC000://Vx=random&NN
V[(opcode>>8)&0x000F]=Math.floor((Math.random() * 255))&((opcode)&0x00FF);
pc+=2;
break;
case 0xD000://DXYN draw(Vx, Vy, N)
V[0xF] = 0x0;
draw(V[(opcode>>8)&0x000F], V[(opcode>>4)&0x000F], (opcode)&0x000F);
pc+=2;
break;
case 0xE000://keypad
switch(opcode & 0x00FF){// EX9E: Skips the next instruction if the key stored in VX is pressed
case 0x009E:
if(parseInt(key[V[(opcode & 0x0F00) >> 8]]) != 0){
// alert();
pressedKey=null;
keyDown=false;
pc+=2;
}else{}
pc+=2;
break;
case 0x00A1:
if(parseInt(key[V[(opcode & 0x0F00) >> 8]]) == 0){
// alert();
pressedKey=null;
keyDown=false;
pc+=2;
}else{}
pc+=2;
break;
}
break;
case 0xF000:
switch(opcode & 0x00FF){
case 0x0007:
V[(opcode>>8)&0x000F]=delay_timer;
pc+=2;
break;
case 0x000A:
if(keyDown==true){
V[(opcode>>8)&0x000F]=pressedKey;pc+=2;
}else{alert("PAUSE");}
break;
case 0x0015:
delay_timer=V[(opcode>>8)&0x000F];
pc+=2;
break;
case 0x0018:
sound_timer=V[(opcode>>8)&0x000F];
pc+=2;
break;
case 0x001E:
I+=V[(opcode>>8)&0x000F];
pc+=2;
break;
case 0x0029:
I=V[(opcode>>8)&0x000F]*5;
pc+=2;
break;
case 0x0033:
memory[I] = V[(opcode & 0x0F00) >> 8] / 100;
memory[I + 1] = (V[(opcode & 0x0F00) >> 8] / 10) % 10;
memory[I + 2] = (V[(opcode & 0x0F00) >> 8] % 100) % 10;
pc += 2;
break;
case 0x0055:
for (var i = 0; i <= ((opcode & 0x0F00) >> 8); i+=2){
memory[I + i] = V[i];
I++;
}
pc+=2;
break;
case 0x0065:
for (var i = 0; i <= ((opcode & 0x0F00) >> 8); i+=2){
V[i] = (memory[I+i]<<8)&memory[I+(i+1)];
I++;
}
pc += 2;
break;
}
break;
default:
console.log("UNSUPPORTED OPCODE- "+(opcode).toString(16));console.log("");
break;
}
// console.log(opcode.toString(16));
if(delay_timer > 0){delay_timer-=1;}
if(sound_timer > 0){if(sound_timer == 1){console.log("BEEP!");sound_timer-=1;}}
// keyDown=false;
// pressedKey=null;
}
</script>