手把手教你编写游戏模拟器 - Chip8篇(3)

 

手把手教你编写游戏模拟器 - Chip8篇(3)

 

 

 

翻译整理分析:by Yiran Xie

 

*如要转载请附上本文链接

 

书接上文(第二篇),下面简单讨论下chip8模拟器剩余的main.cpp文件。main中包含了opengl的glut编程来实现图像与输入系统,我对这块一点经验也没有,所以也是摸着石头过河。如有说错的请见谅=.=

 

首先安装opengl的glut库,简单地总结一下吧。

下载地址http://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip 

Windows+ VC环境下安装的步骤: 
1、将下载的压缩包解开,将得到5个文件 
2、把gl.h放到visual studio的vc的include下,例如$\Microsoft Visual Studio 9.0\VC\include 
3、将glut.lib和glut32.lib放到静态函数库所在文件夹,例如$\Microsoft Visual Studio 9.0\VC\lib
4、将glut.dll和glut32.dll放到windows\system32下 

最后,当前project->linker->input->Additional Dependencies,加入glut32.lib glu32.lib。这样应该就能顺利编译了.

具体main.cpp代码如下,我做了下删减(去除了一些typedef和被作者抛弃的旧版的函数)这样看着更清楚一些。

复制代码
///
// Project description
// Name: myChip8
//
// Author: Laurence Muller
// Contact: laurence.muller@gmail.com
//
// License: GNU General Public License (GPL) v2 
// ( http://www.gnu.org/licenses/old-licenses/gpl-2.0.html )
//
// Copyright (C) 2011 Laurence Muller / www.multigesture.net
///
#include <stdio.h>
#include <stdlib.h>
#include <glut.h>
#include "chip8.h"

// Display size
#define SCREEN_WIDTH 64
#define SCREEN_HEIGHT 32

chip8 myChip8;//作为全局变量
int ratio = 10;//window窗口大小和实际模拟器像素之间的比例

//windows窗口大小
int display_width  = SCREEN_WIDTH * ratio;
int display_height = SCREEN_HEIGHT * ratio;

void display();
void reshape_window(GLsizei w, GLsizei h);
void keyboardUp(unsigned char key, int x, int y);
void keyboardDown(unsigned char key, int x, int y);

unsigned char screenData[SCREEN_HEIGHT][SCREEN_WIDTH][3];
void setupTexture();

void DisplayAndSleep()
{
    display();
    _sleep(2);
}

int main(int argc, char **argv) 
{        
    if(argc < 2)
    {
        printf("Usage: myChip8.exe chip8application\n\n");
        return 1;
    }

    //读取rom
    if(!myChip8.loadApplication(argv[1]))        
        return 1;
        
    // Setup glut
    glutInit(&argc, argv);          
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

    glutInitWindowSize(display_width, display_height);
    glutInitWindowPosition(320, 320);
    glutCreateWindow("myChip8 by Laurence Muller");
    
   // 把我们的回调函数在这儿注册给glut glutDisplayFunc(display); glutIdleFunc(DisplayAndSleep); glutReshapeFunc(reshape_window); glutKeyboardFunc(keyboardDown); glutKeyboardUpFunc(keyboardUp); setupTexture(); glutMainLoop();
return 0; } // Setup Texture void setupTexture() { // Clear screen for(int y = 0; y < SCREEN_HEIGHT; ++y) for(int x = 0; x < SCREEN_WIDTH; ++x) screenData[y][x][0] = screenData[y][x][1] = screenData[y][x][2] = 0; // Create a texture glTexImage2D(GL_TEXTURE_2D, 0, 3, SCREEN_WIDTH, SCREEN_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)screenData); // Set up the texture glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // Enable textures glEnable(GL_TEXTURE_2D); } void updateTexture(const chip8& c8) { // Update pixels for(int y = 0; y < 32; ++y) for(int x = 0; x < 64; ++x) if(c8.gfx[(y * 64) + x] == 0) screenData[y][x][0] = screenData[y][x][1] = screenData[y][x][2] = 0; // Disabled else screenData[y][x][0] = screenData[y][x][1] = screenData[y][x][2] = 255; // Enabled // Update Texture glTexSubImage2D(GL_TEXTURE_2D, 0 ,0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)screenData); glBegin( GL_QUADS ); glTexCoord2d(0.0, 0.0); glVertex2d(0.0, 0.0); glTexCoord2d(1.0, 0.0); glVertex2d(display_width, 0.0); glTexCoord2d(1.0, 1.0); glVertex2d(display_width, display_height); glTexCoord2d(0.0, 1.0); glVertex2d(0.0, display_height); glEnd(); } void display() { myChip8.emulateCycle(); if(myChip8.drawFlag) { // Clear framebuffer glClear(GL_COLOR_BUFFER_BIT); updateTexture(myChip8); // Swap buffers! glutSwapBuffers(); // Processed frame myChip8.drawFlag = false; } } void reshape_window(GLsizei w, GLsizei h) { glClearColor(0.0f, 0.0f, 0.5f, 0.0f); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, w, h, 0); glMatrixMode(GL_MODELVIEW); glViewport(0, 0, w, h); // Resize quad display_width = w; display_height = h; } void keyboardDown(unsigned char key, int x, int y)//更新按键按下信息 { if(key == 27)//如果是esc按键,则退出。否则,更新相应chip8.key[]中的对应按键 exit(0); /*人为定义的键盘布局如下: 1234 qwer asdf zxcv*/ if(key == '1') myChip8.key[0x1] = 1; else if(key == '2') myChip8.key[0x2] = 1; else if(key == '3') myChip8.key[0x3] = 1; else if(key == '4') myChip8.key[0xC] = 1; else if(key == 'q') myChip8.key[0x4] = 1; else if(key == 'w') myChip8.key[0x5] = 1; else if(key == 'e') myChip8.key[0x6] = 1; else if(key == 'r') myChip8.key[0xD] = 1; else if(key == 'a') myChip8.key[0x7] = 1; else if(key == 's') myChip8.key[0x8] = 1; else if(key == 'd') myChip8.key[0x9] = 1; else if(key == 'f') myChip8.key[0xE] = 1; else if(key == 'z') myChip8.key[0xA] = 1; else if(key == 'x') myChip8.key[0x0] = 1; else if(key == 'c') myChip8.key[0xB] = 1; else if(key == 'v') myChip8.key[0xF] = 1; //printf("Press key %c\n", key); } void keyboardUp(unsigned char key, int x, int y)//更新按键释放信息 { if(key == '1') myChip8.key[0x1] = 0; else if(key == '2') myChip8.key[0x2] = 0; else if(key == '3') myChip8.key[0x3] = 0; else if(key == '4') myChip8.key[0xC] = 0; else if(key == 'q') myChip8.key[0x4] = 0; else if(key == 'w') myChip8.key[0x5] = 0; else if(key == 'e') myChip8.key[0x6] = 0; else if(key == 'r') myChip8.key[0xD] = 0; else if(key == 'a') myChip8.key[0x7] = 0; else if(key == 's') myChip8.key[0x8] = 0; else if(key == 'd') myChip8.key[0x9] = 0; else if(key == 'f') myChip8.key[0xE] = 0; else if(key == 'z') myChip8.key[0xA] = 0; else if(key == 'x') myChip8.key[0x0] = 0; else if(key == 'c') myChip8.key[0xB] = 0; else if(key == 'v') myChip8.key[0xF] = 0; }
复制代码

代码理解起来难度不大,主要的还是对于glut32相关库函数的熟悉,这块我目前了解的极少,打算有空时进一步研究一下(有待继续更新)。不过到了这一步,openGL or glut32并不是唯一的选择,用SDL,DirectX或者其他库也能实现同样的功能。

 

举个例子,我前两天写了个c的版本,已经成功移植到了自己的OS简单的内核上(这个内核在编写时参考了Osask和OrangeOS,留待以后再细说)。主要是用到了文件读取\释放、窗口新建、绘画与刷新、定时器、按键读取这些API。换句话说,只要任何库能实现这些API,都可以用来作为glut32的替代实现。

 

其中有几点是我在编写过程中额外注意到的,一是要想办法去降低帧数(使之趋近于60hz),比如我是用了计时器;二是如果你用的是像我这样的山寨的显示API的话,那么有可能编写会在刷新时出现大面积闪烁的情况。我的解决办法是尽量不要全局刷新,而仅作局部刷新。比如维持一个gfx_old[]数组,每次比对gfx[]与gfx_old[],仅刷新发生了变化的点(从亮变暗或从暗变亮的点)

 

最后补一张,chip8模拟器跑在我的OS内核上的图。图为同时开启三个进程,运行三款不同的ROM

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值