/ Vijos / 讨论 / 游戏 /

Tetris alpha

无聊一个下午写出来的游戏……编译请加上-lopengl32 -lgdi32
#include <windows.h>
#include <gl\gl.h>
#include <ctime>
#include <cmath>
#include <map>
using namespace std;
#ifdef LOCAL
# include <iostream>
using namespace std;
#endif

RECT Client;
HWND hWnd;

struct TVec {
float X, Y;
TVec () : X (0.0f), Y (0.0f) {
}
TVec (float x, float y) : X (x), Y (y) {
}
TVec (const TVec &src) : X (src.X), Y (src.Y) {
}

float LengthSquared () const {
return X * X + Y * Y;
}
float Length () const {
return sqrtf (X * X + Y * Y);
}
};
const float LightPer = 0.4f;
struct TColor {
float A, R, G, B;
TColor () : A (1.0f), R (0.0f), G (0.0f), B (0.0f) {
}
TColor (float a, float r, float g, float b) : A (a), R (r), G (g), B (b) {
}
TColor (const TColor &src) : A (src.A), R (src.R), G (src.G), B (src.B) {
}

TColor Lighter () const {
return TColor (A, R + (1 - R) * LightPer, G + (1 - G) * LightPer, B + (1 - B) * LightPer);
}
TColor Darker () const {
return TColor (A, R * (1.0f - LightPer), G * (1.0f - LightPer), B * (1.0f - LightPer));
}

void Set () const {
glColor4f (R, G, B, A);
}
};

TVec GetMouse () {
POINT p;
GetCursorPos (&p);
ScreenToClient (hWnd, &p);
return TVec (p.x / (float) Client.right, p.y / (float) Client.bottom);
}

class TBlock {
public:
TBlock () : clr (
1.0f,
rand () / (float) RAND_MAX,
rand () / (float) RAND_MAX,
rand () / (float) RAND_MAX) {
}
TBlock (const TColor &c) : clr (c) {
}
TBlock (const TBlock &src) : clr (src.clr) {
}

void Render (TVec v1, TVec v2) {
glBegin (GL_QUADS);
clr.Set ();
glVertex2f (v1.X, v1.Y);
glVertex2f (v2.X, v1.Y);
glVertex2f (v2.X, v2.Y);
glVertex2f (v1.X, v2.Y);
glEnd ();

float xb = (v2.X - v1.X) * borderPer, yb = (v2.Y - v1.Y) * borderPer;

glBegin (GL_POLYGON);
clr.Lighter ().Set ();
glVertex2f (v1.X, v1.Y);
glVertex2f (v2.X, v1.Y);
glVertex2f (v2.X - xb, v1.Y + yb);
glVertex2f (v1.X + xb, v1.Y + yb);
glVertex2f (v1.X + xb, v2.Y - yb);
glVertex2f (v1.X, v2.Y);
glEnd ();

glBegin (GL_POLYGON);
clr.Darker ().Set ();
glVertex2f (v2.X, v2.Y);
glVertex2f (v2.X, v1.Y);
glVertex2f (v2.X - xb, v1.Y + yb);
glVertex2f (v2.X - xb, v2.Y - yb);
glVertex2f (v1.X + xb, v2.Y - yb);
glVertex2f (v1.X, v2.Y);
glEnd ();
}

TColor GetColor () const {
return clr;
}
void SetColor (const TColor &c) {
clr = c;
}
private:
TColor clr;
const float borderPer = 0.1f;
};

class TBlockManager {
public:
TBlockManager () : m () {
}
void AddBlock (TBlock *b) {
BlockIterator bit = m.find (b);
if (bit == m.end ()) {
m.insert (BlockType (b, TBlockData (b)));
# ifdef LOCAL
cout << "BLOCK ADDED NOW " << m.size () << endl;
# endif
} else {
bit -> second.AddReference ();
}
}
void RemoveBlock (TBlock *b) {
BlockIterator bit = m.find (b);
if (!bit -> second.RemoveReference ()) {
delete b;
m.erase (bit);
# ifdef LOCAL
cout << "BLOCK DELETED NOW " << m.size () << endl;
# endif
}
}
private:
struct TBlockData {
TBlock *Block;
int ReferenceCount;
TBlockData (TBlock block) : Block (block), ReferenceCount (1) {
}
void AddReference () {
ReferenceCount++;
# ifdef LOCAL
cout << "REFERENCE ADDED NOW " << ReferenceCount << endl;
# endif
}
bool RemoveReference () {
ReferenceCount--;
# ifdef LOCAL
cout << "REFERENCE REMOVED NOW " << ReferenceCount << endl;
# endif
return ReferenceCount > 0;
}
};
typedef map<TBlock
, TBlockData> :: iterator BlockIterator;
typedef map<TBlock*, TBlockData> :: value_type BlockType;
map<TBlock*, TBlockData> m;
};
class TGame;
class TFallingObject {
friend class TGame;
public:
TFallingObject () : b (new TBlock ()), mapidx (2), x (0), y (0), rot (0), p (NULL) {
}
TFallingObject (int index) : b (new TBlock ()), mapidx (index), rot (0), p (NULL) {
}
TFallingObject (const TFallingObject &src) :
b (new TBlock (*src.b)), mapidx (src.mapidx), rot (0), p (NULL) {
}
~TFallingObject () {
if (b) {
delete b;
}
}

void RandomPosition (int xstart, int xend, int ystart, int yend) {
x = xstart + rand () % (xend - xstart - (ex[mapidx] - sx[mapidx]) + 2) - sx[mapidx];
y = ystart + rand () % (yend - ystart - (ey[mapidx] - sy[mapidx]) + 2) - sy[mapidx];
}
void SetPosition (int xx, int yy) {
x = xx;
y = yy;
}
bool Rotate ();
bool MoveLeft ();
bool MoveRight ();
bool Fall ();
void Fix ();
bool IsBlock (int x, int y) const {
return stdmap[mapidx][rot] & (1 << (15 - 4 * y - x));
}
int MapIndex () const {
return mapidx;
}
int RotateState () const {
return rot;
}

void Render () const;
private:
TBlock *b;
int mapidx, x, y, rot;
static bool IsBlock (int x, int y, int map) {
return map & (1 << (15 - 4 * y - x));
}
const short stdmap[7][4] {
{ 0x2222, 0x00F0, 0x2222, 0x00F0 },
{ 0x0270, 0x0232, 0x0072, 0x0262 },
{ 0x0660, 0x0660, 0x0660, 0x0660 },
{ 0x0470, 0x0322, 0x0071, 0x0226 },
{ 0x0170, 0x0223, 0x0074, 0x0622 },
{ 0x0630, 0x1320, 0x0630, 0x1320 },
{ 0x0360, 0x4620, 0x0360, 0x4620 }
};
const int
sx[7] { 2, 1, 1, 1, 1, 1, 1 },
sy[7] { 0, 1, 1, 1, 1, 1, 1 },
ex[7] { 3, 4, 3, 4, 4, 4, 4 },
ey[7] { 4, 3, 3, 3, 3, 3, 3 };
TGame *p;
};
class TGame {
friend class TFallingObject;
public:
typedef void (Callback) ();
TGame (int width, int height, float bwidth, float bheight, float xstart, float ystart) :
w (width), h (height), bw (bwidth), bh (bheight),
xs (xstart), ys (ystart), map (new TBlock
[width]), man (new TBlockManager),
cur (NULL), ofix (NULL), oover (NULL), over (false) {
int sz = sizeof (void
) * height;
for (int x = 0; x < width; x++) {
map[x] = new TBlock*[height];
ZeroMemory (map[x], sz);
}
}
~TGame () {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (map[x][y]) {
man -> RemoveBlock (map[x][y]);
}
}
}
free (map);
if (cur) {
delete cur;
}
delete man;
}

void Render () const {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (map[x][y]) {
map[x][y] -> Render (
TVec (xs + bw * x, ys + bh * y),
TVec (xs + bw * (x + 1), ys + bh * (y + 1)));
}
}
}
if (cur) {
cur -> Render ();
}
glBegin (GL_LINE_LOOP);
glColor3f (1.0f, 1.0f, 1.0f);
glVertex2f (xs, ys);
glVertex2f (xs + w * bw, ys);
glVertex2f (xs + w * bw, ys + h * bh);
glVertex2f (xs, ys + h * bh);
glEnd ();
}

void Update () {
if (over) {
return;
}
if (cur) {
if (!cur -> Fall ()) {
if (ofix) {
ofix ();
}
}
}
}

void CheckLine (int y) {
for (int x = 0; x < w; x++) {
if (!map[x][y]) {
return;
}
}
# ifdef LOCAL
cout << "Line Deleted!\n";
# endif
for (int x = 0; x < w; x++) {
man -> RemoveBlock (map[x][y]);
map[x][y] = NULL;
int dist = 1;
for (int ay = y - 1; ay >= 0; ay--) {
if (map[x][ay]) {
map[x][ay + 1] = map[x][ay];
map[x][ay] = NULL;
}
}
}
}

TFallingObject *GetCurrentFalling () const {
return cur;
}
void SetCurrentFalling (TFallingObject *obj) {
if (over) {
return;
}
if (cur) {
cur -> p = NULL;
}
if (obj -> p) {
throw;
}
if (obj) {
obj -> p = this;
}
cur = obj;
}

TBlock **operator {
return map[x];
}
void SetOnFix (Callback o) {
ofix = o;
}
void SetOnOver (Callback o) {
oover = o;
}
private:
int w, h;
float bw, bh, xs, ys;
TBlock ***map;
TBlockManager *man;
TFallingObject *cur;
Callback ofix, oover;
bool over;
};
void TFallingObject :: Render () const {
if (!b || !p) {
return;
}
for (int by = 0; by < 4; by++) {
for (int bx = 0; bx < 4; bx++) {
if (IsBlock (bx, by)) {
# ifndef LOCAL
if (x + bx >= 0 && x + bx < p -> w && y + by >= 0 && y + by <= p -> h) {
# endif
b -> Render (
TVec (p -> xs + p -> bw * (bx + x), p -> ys + p -> bh * (by + y)),
TVec (p -> xs + p -> bw * (bx + x + 1), p -> ys + p -> bh * (by + y + 1)));
# ifndef LOCAL
}
# endif
}
}
}
}
bool TFallingObject :: Rotate () {
int nrot = (rot + 1) % 4;
for (int by = 0; by < 4; by++) {
for (int bx = 0; bx < 4; bx++) {
if (IsBlock (bx, by, stdmap[mapidx][nrot])) {
if (x + bx >= p -> w || x + bx < 0 || y + by >= p -> h) {
return false;
}
if (y + by >= 0 && p -> map[x + bx][y + by]) {
return false;
}
}
}
}
rot = nrot;
return true;
}
bool TFallingObject :: MoveRight () {
if (!p) {
return false;
}
for (int by = 0; by < 4; by++) {
for (int bx = 0; bx < 4; bx++) {
if (IsBlock (bx, by)) {
if (x + bx + 2 > p -> w) {
return false;
}
if (y + by >= 0 && p -> map[x + bx + 1][y + by]) {
return false;
}
}
}
}
x++;
return true;
}
bool TFallingObject :: MoveLeft () {
if (!p) {
return false;
}
for (int by = 0; by < 4; by++) {
for (int bx = 0; bx < 4; bx++) {
if (IsBlock (bx, by)) {
if (x + bx - 1 < 0) {
return false;
}
if (y + by >= 0 && p -> map[x + bx - 1][y + by]) {
return false;
}
}
}
}
x--;
return true;
}
bool TFallingObject :: Fall () {
if (!p) {
return false;
}
for (int by = 0; by < 4; by++) {
for (int bx = 0; bx < 4; bx++) {
if (IsBlock (bx, by)) {
if (y + by + 1 >= 0 && (y + by + 2 > p -> h || p -> map[x + bx][y + by + 1])) {
Fix ();
return false;
}
}
}
}
y++;
return true;
}
void TFallingObject :: Fix () {
if (!p) {
return;
}
# ifdef LOCAL
b -> SetColor (TColor (1.0f, 1.0f, 1.0f, 1.0f));
# endif
bool o = false;
for (int by = 0; by < 4; by++) {
for (int bx = 0; bx < 4; bx++) {
if (IsBlock (bx, by)) {
if (y + by < 0) {
if (!o) {
if (p -> oover) {
p -> oover ();
}
o = p -> over = true;
}
} else {
p -> map[x + bx][y + by] = b;
p -> man -> AddBlock (b);
}
}
}
}
if (!o) {
for (int by = 0; by < 4; by++) {
if (y + by >= 0 && y + by < p -> h) {
p -> CheckLine (y + by);
}
}
}
p -> cur = NULL;
p = NULL;
b = NULL;
delete this;
}

const int GameWidth = 10, GameHeight = 20;

TGame *t;
void OnFixObj () {
# ifdef LOCAL
cout << "FIXED!\n";
# endif
TFallingObject *o = new TFallingObject (rand () % 7);
o -> SetPosition ((GameWidth - 4) >> 1, -4);
t -> SetCurrentFalling (o);
}
void OnOver () {
# ifdef LOCAL
cout << "OVER!\n";
# endif
}
void Init () {
srand (clock ());
t = new TGame (GameWidth, GameHeight, 0.08f, 0.04f, 0.1f, 0.1f);
OnFixObj ();
t -> SetOnFix (OnFixObj);
t -> SetOnOver (OnOver);
}
float td;
void OnKeyPress (int key) {
TFallingObject *o = t -> GetCurrentFalling ();
if (!o) {
return;
}
switch (key) {
case VK_RIGHT:
o -> MoveRight ();
break;
case VK_LEFT:
o -> MoveLeft ();
break;
case VK_DOWN:
td = 0;
if (!o -> Fall ()) {
OnFixObj ();
}
break;
case VK_UP:
o -> Rotate ();
break;
}
}
void Update (float time) {
td += time;
while (td > 1.0f) {
t -> Update ();
td -= 1.0f;
}
}
void Render () {
t -> Render ();
}
void Dispose () {
delete t;
}

LRESULT CALLBACK WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CLOSE:
PostQuitMessage (0);
return 0;
case WM_SIZE:
GetClientRect (hWnd, &Client);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glViewport (0, 0, Client.right, Client.bottom);
glOrtho (0, 1, 1, 0, -100, 100);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
return 0;
case WM_KEYDOWN:
OnKeyPress (wParam);
return 0;
default:
return DefWindowProc (hWnd, msg, wParam, lParam);
}
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow) {
WNDCLASS wc;
HDC hDC;
HGLRC hRC;
MSG msg;
wc.style = CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "tetrisgame";
RegisterClass (&wc);
hWnd = CreateWindow (
"tetrisgame", "Tetris",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 600,
NULL, NULL, hInstance, NULL);

PIXELFORMATDESCRIPTOR pfd;
int iFormat;
hDC = GetDC (hWnd);
ZeroMemory (&pfd, sizeof (pfd));
pfd.nSize = sizeof (pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
iFormat = ChoosePixelFormat (hDC, &pfd);
SetPixelFormat (hDC, iFormat, &pfd);
hRC = wglCreateContext (hDC);
wglMakeCurrent (hDC, hRC);

GetClientRect (hWnd, &Client);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glViewport (0, 0, Client.right, Client.bottom);
glOrtho (0, 1, 1, 0, -100, 100);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();

long last, cur = clock ();
Init ();
while (true) {
if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
break;
}
TranslateMessage (&msg);
DispatchMessage (&msg);
} else {
glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
glClear (GL_COLOR_BUFFER_BIT);

last = cur;
cur = clock ();
Update ((cur - last) / (float) CLOCKS_PER_SEC);
Render ();

SwapBuffers (hDC);
}
}
Dispose ();

wglMakeCurrent (NULL, NULL);
wglDeleteContext (hRC);
ReleaseDC (hWnd, hDC);

DestroyWindow (hWnd);
return msg.wParam;
}

7 条评论

  • 1