Tetris(俄罗斯方块)
一天有个小朋友问我OpenGL俄罗斯方块怎么写。
俄罗斯方块分成两部分游戏逻辑和画面渲染.
1. 游戏逻辑
一个简单的俄罗斯方块的逻辑部分需要考虑的情况如下:
1. 方块的表示(坐标, 旋转, 上下左右移动)
2. 格子的状态记录, 移动中的方块和边界的碰撞检测和已固定的方块的碰撞检测
3. 行满检测与消除
具体的面向对象实现如下:
class Game{
public:
int tiles[20][10] = {0};
void new_tile() {
tile << 5, 0, random()%7, random()%4;
}
void write_tile() {
if(!running || !started) return ;
for(int i = 0 ; i < 4; i++) {
tiles[tile[1]+tileMap[tile[2]][tile[3]][i][1]][tile[0]+tileMap[tile[2]][tile[3]][i][0]] = tile[2] + 1;
}
}
void wipe_tile() {
if(!running || !started) return ;
for(int i = 0 ; i < 4; i++) {
tiles[tile[1]+tileMap[tile[2]][tile[3]][i][1]][tile[0]+tileMap[tile[2]][tile[3]][i][0]] = 0;
}
}
void rotation() {
if(!running || !started) return ;
tile[3] = (tile[3]+1)%4;
for(int i = 0 ; i < 4; i++) {
std::pair<int,int> pos= {
tile[0]+tileMap[tile[2]][tile[3]][i][0],
tile[1]+tileMap[tile[2]][tile[3]][i][1],
};
if(pos.first < 0 || pos.first >= 10 || pos.second >= 20 || tiles[pos.second][pos.first]) {
tile[3] = (tile[3]+3)%4;
break;
}
}
}
bool move(int x, int y, bool passive = false) {
if(!running || !started) return true;
tile[0] += x;
tile[1] += y;
for(int i = 0 ; i < 4; i++) {
std::pair<int,int> pos= {
tile[0]+tileMap[tile[2]][tile[3]][i][0],
tile[1]+tileMap[tile[2]][tile[3]][i][1],
};
if(pos.first < 0 || pos.first >= 10 || pos.second >= 20 || tiles[pos.second][pos.first]) {
tile[0] -= x; tile[1] -= y;
if(passive) {
write_tile();
if(tile[1] != 0) new_tile();
else {started = false; write_end(8);}
}
return false;
}
}
check_row();
return true;
}
void update() {
if(!running || !started) return ;
if(frame_cnt++ == 24) {
wipe_tile();
move(0,1,true);
write_tile();
frame_cnt = 0;
}
}
void restart() {
memset(tiles, 0, sizeof(int)*200);
new_tile();
started = true;
running = true;
}
void resume_or_pause() {
running = !running;
}
void write_end(int i = 8) {
memset(tiles[std::max(i-1,0)], 0, 4*10*7);
tiles[i+1][0] = tiles[i+3][0] = 1;
tiles[i+0][0] = tiles[i+0][1] = tiles[i+0][2] = 1;
tiles[i+2][0] = tiles[i+2][1] = tiles[i+2][2] = 1;
tiles[i+4][0] = tiles[i+4][1] = tiles[i+4][2] = 1;
tiles[i+0][3] = tiles[i+1][3] = tiles[i+2][3] = tiles[i+3][3] = tiles[i+4][3] = 2;
tiles[i+0][6] = tiles[i+1][6] = tiles[i+2][6] = tiles[i+3][6] = tiles[i+4][6] = 2;
tiles[i+1][4] = tiles[i+2][4] = tiles[i+2][5] = tiles[i+3][5] = 2;
tiles[i+0][7] = tiles[i+1][7] = tiles[i+2][7] = tiles[i+3][7] = tiles[i+4][7] = 3;
tiles[i+0][8] = tiles[i+4][8] = tiles[i+1][9] = tiles[i+2][9] = tiles[i+3][9] = 3;
}
Eigen::Vector4i tile; // (x, y, type, rotation)
private:
// 一个二维数组表示所有可能出现的方块和方向。
static int tileMap[7][4][4][2];
void check_row() {
for(int i = 19, ii = 19; i >= 0; i--) {
int tile_cnt = 0;
for(int j = 0; j < 10; j++) tile_cnt += tiles[i][j] > 0;
for(int j = 0; j < 10; j++) tiles[ii][j] = tiles[i][j] ;
if(tile_cnt != 10) ii--;
}
}
int frame_cnt = 0;
bool running = false, started = false;
} game;
int Game::tileMap[7][4][4][2] = {
{ 0, 0, -1, 0, 1, 0, -1, -1, // "L"
0, 1, 0, 0, 0, -1, 1, -1,
1, 1, -1, 0, 0, 0, 1, 0,
-1, 1, 0, 1, 0, 0, 0, -1},
{ 0, 0, -1, -1, -1, 0, 0, -1, //"O"
0, 0, -1, -1, -1, 0, 0, -1,
0, 0, -1, -1, -1, 0, 0, -1,
0, 0, -1, -1, -1, 0, 0, -1},
{ 0, 0, 1, 0, -1, 0, -2, 0, //"I"
0, 0, 0, -2, 0, -1, 0, 1,
0, 0, 1, 0, -1, 0, -2, 0,
0, 0, 0, -2, 0, -1, 0, 1},
{ 0, 0, 1, 0, -1, -1, 0, -1, //"S"
0, 0, 0, 1, 1, 0, 1, -1,
0, 0, 1, 0, -1, -1, 0, -1,
0, 0, 0, 1, 1, 0, 1, -1},
{ 0, 0, -1, 0, 1, 0, 1, -1, //"J"
0, 0, 0, 1, 0, -1, 1, 1,
0, 0, 1, 0, -1, 0, -1, 1,
0, 0, 0, 1, 0, -1, -1, -1},
{ 0, 0, -1, 0, 0, -1, 1, -1, //"Z"
0, -1, 0, 0, 1, 0, 1, 1,
0, 0, -1, 0, 0, -1, 1, -1,
0, -1, 0, 0, 1, 0, 1, 1},
{ 0, 0, 1, 0, -1, 0, 0, -1, //"T"
0, 0, 0, 1, 0, -1, 1, 0,
0, 0, 0, 1, -1, 0, 1, 0,
-1, 0, 0, 1, 0, -1, 0, 0}
};
2. 渲染实现
对于渲染的要求,只使用一组着色器实现,即通过Uniform传所有格子的状态,具体如下:
// vertex shader
#version 330 core
in vec2 position;
out vec2 pos;
void main()
{
pos = vec2(position.x*1.14,position.y*-1.09);
gl_Position = vec4(position, 0.0, 1.0);
} ;
// fragment shader
#version 330 core
out vec4 outColor;
in vec2 pos;
uniform int tile[200];
uniform sampler2D tileTex;
void main()
{
vec2 border = smoothstep(-0.1, 0.0, -abs(sin(3.1415926*pos*vec2(5.0,10.0))));
if(max(abs(pos.x),abs(pos.y))>1.002) {outColor.xyz = texture(tileTex,vec2(pos.x/2.28 + 0.5, 360.0/393.0*(pos.y/2.18 + 0.5 )) ).xyz;return;}int tile_id = int(floor(10.0*pos.y+10.0)/*xiconxi.github.io*/*10)+int(floor(5.0*pos.x+5.0));
tile_id = tile[tile_id<200&&tile_id>=0?tile_id:0];
vec3 tile_color = texture(tileTex,vec2( (tile_id-1+mod(abs(pos.x*5),1.0))/7.0,360.0/393.0+33.0/393.0*mod(abs(pos.y*10.0), 1.0))).xyz;
tile_color = tile_id == 0 ? vec3(0.55)*length(tile_color): tile_color;
outColor.xyz = mix(tile_color ,vec3(0.0),max(border.x,border.y));
} ;
总体渲染效果如下:
具体代码在Github::Tetris
Tetris(俄罗斯方块)的更多相关文章
- [转]Tetris(俄罗斯方块) in jQuery/JavaScript!
本文转自:http://pwwang.com/2009/10/25/tetris-in-jquery-javascript/ All in jQuery/JavaScript + HTML! Demo ...
- x01.Tetris: 俄罗斯方块
最强大脑有个小孩玩俄罗斯方块游戏神乎其技,那么,就写一个吧,玩玩而已. 由于逻辑简单,又作了一些简化,所以代码并不多. using System; using System.Collections.G ...
- Java项目--俄罗斯方块
Java项目--俄罗斯方块 百度盘链接 链接:http://pan.baidu.com/s/1mhQ9SYc 密码:9ujo 一.心得 二.游戏实例 游戏截图 目录结构 三.代码 1.主界面 Tetr ...
- 【C语言程序设计】小游戏之俄罗斯方块(二)!适合初学者上手、练手!
第二篇,主要实现俄罗斯方块中的主体部分,包括容器的数据结构以及容器的相关操作,特别是大方块和容器之间的交互逻辑,包括碰撞检测,消除检测等等. 1. 容器的表示 大方块的实现涉及到位运算,而容器同样如此 ...
- 怀旧浪潮来袭,小霸王游戏、windows95......曾经的经典哪些能戳中你的心怀?
随着前两天上架的 Rewound 在 iPhone 上复刻了 iPod Classic为大家掀起一场怀旧浪潮,那么除了 Rewound还有什么经典?今天我们就来怀旧一下那些曾经的经典.80经典小霸王游 ...
- Flutter 2.2 更新详解
Flutter 2.2 版已正式发布!要获取新版本,您只需切换到 stable 渠道并更新目前安装的 Flutter,或前往 flutter.cn/docs/get-started 从头开始安装. 虽 ...
- 俄罗斯方块 Tetris
今天,为大家带来一个用Qt C++ (Windows环境下)做的一个简易俄罗斯方块小游戏 思路和模块介绍都在注释里面,其次就是一些项目中遇到的问题以及解决方案,在后面部分说明. 一.效果 测试图样 Q ...
- electron写俄罗斯方块游戏(Tetris)
背景 在折腾ES6,突然想起大学时用c语言写过俄罗斯方块,本项目中主要是利用ES6的Class特性进行面向对象编程.项目采用node.js v6.2.0 + electron v1.1.0 进行桌面开 ...
- 用Shell实现俄罗斯方块代码(Tetris.sh)
本代码来源于网络: 文件下载地址:http://files.cnblogs.com/files/DreamDrive/Tetris.sh #!/bin/bash # Tetris Game # 10. ...
随机推荐
- 使用 FRP 反向代理实现 Windows 远程连接
互联网普及率的日渐攀升与 IPv4 资源的持续减少,现在大部分家庭宽带都不会分配公网 IP ,这使一些网络应用的实现多了些困难,像个人的 NAS 和一些智能家居设备.对于分配公网 IP ,各地运营商的 ...
- iOS: 聊聊 Designated Initializer(指定初始化函数):NS_DESIGNATED_INITIALIZER
总结:指定函数的调用规则: 初始化函数的调用顺序与初始化顺序相反. 上面关于指定初始化的规则讲了那么多,其实可以归纳为两点: 便利初始化函数只能调用自己类中的其他初始化方法 指定初始化函数才有资格调用 ...
- Python中的赋值和拷贝
赋值 在python中,赋值就是建立一个对象的引用,而不是将对象存储为另一个副本.比如: >>> a=[1,2,3] >>> b=a >>> c= ...
- 通过golang 查询impala
cloudera官方没有提供impala基于golang的驱动,github有github.com/bippio/go-impala package main import ( "conte ...
- 【[HNOI2016]序列】
莫队好题啊 莫队来做这个题的难点就是考虑如何在\(O(1)\)时间内由\([l,r]\)转移到\([l,r+1]\) 显然加入\(r+1\)这个数之后会和之前所有的位置都产生一个区间,就是要去快速求出 ...
- Solr服务在Linux上的搭建详细教程
一.系统环境 注:欢迎大家转载,非商业用途请在醒目位置注明本文链接和作者名dijia478即可,商业用途请联系本人dijia478@163.com. CentOS-6.7-i386-bin-DVD1 ...
- HustOJ平台搭建
HustOJ平台搭建非常简单,首先为了排除一些不必要的故障,可以使用阿里云的服务器更新系统盘让系统盘初始化保持在没有其他包依赖的环境下及其使用root用户. 1.针对Ubuntu14.04(根据官方文 ...
- 集合之Iterator
迭代对于我们搞Java的来说绝对不陌生.我们常常使用JDK提供的迭代接口进行Java集合的迭代. Iterator iterator = list.iterator(); while(iterator ...
- 郑重推荐开源CANopen协议栈CANFestival(LGPL许可)!!!!!!!!
郑重推荐开源CANopen协议栈CANFestival(LGPL许可)!!!!!!!!(这条文章已经被阅读了 次) 时间:2010/03/04 06:47am 来源:winshton [这个贴子最后由 ...
- Linux学习笔记(第七章)
目录相关操作 pwd -P 显示出实际工作目录,pwd 显示链接路径 mkdir -m 配置新建文件的权限 例:mkdir -m 711 testmkdir -p 建立多层目录,无需一层一层建立 rm ...