今天的主題:忍者飛鏢(仍然利用自己寫的向量和矩陣實現)
介紹:飛鏢丟出後能夠返回原來的位置
注意點:
1.在沒有任何操作的情況下,飛鏢圍繞正方形順時針公轉並自轉
2.按下空格後,飛鏢丟出,延當前的-Y方向運動,可以連續按鍵,直至所有飛鏢全部飛出,正方形無論何種情況都是可以自由移動的
3.飛鏢與屏幕正上方碰撞後,立即返回,朝向爲自己的原定位置(此位置無論飛鏢在與否,都會公轉),到達指定位置後,繼續公轉與自轉
難點:
飛鏢的自轉公轉
先看效果吧
#include <iostream>
#include <windows.h>
#include <vector>
#include "vector2.h"
#include "matrix3.h"
#pragma comment(lib, "Msimg32.lib")
//定義一些窗口常量
#define _CLIENT_PW 640 //屏幕像素寬
#define _CLIENT_PH 480 //屏幕像素高
#define _FRAME_PER_SECOND 40 //屏幕每秒刷新幀數
#define _FRAME_SLEEPTIME (1000/_FRAME_PER_SECOND) //每幀休息時間
#define _PI 3.1415 //pai
//全局窗口句柄
HWND g_hWnd = nullptr;
//窗口活動
BOOL g_Active = FALSE;
//函數聲明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void GameInit();
void GameRun();
void GameEnd();
//程序入口點
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//填充窗口類別結構體
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszMenuName = nullptr;
wc.lpszClassName = __TEXT("My3D");
//註冊窗口類別結構體
RegisterClass(&wc);
//根據客戶區矩形計算窗口矩形
int screen_pw = GetSystemMetrics(SM_CXSCREEN);
int screen_ph = GetSystemMetrics(SM_CYSCREEN);
RECT r =
{
(screen_pw - _CLIENT_PW) / 2,
(screen_ph - _CLIENT_PH) / 2,
r.left + _CLIENT_PW,
r.top + _CLIENT_PH
};
//校準窗口矩形
AdjustWindowRect(&r, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE);
//創建窗口
g_hWnd = CreateWindow(
wc.lpszClassName,
__TEXT("My3D"),
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
r.left, r.top, r.right - r.left, r.bottom - r.top, //窗口左上角位置和寬高
HWND_DESKTOP,
NULL,
wc.hInstance,
NULL);
//更新窗口
UpdateWindow(g_hWnd);
//顯示窗口
ShowWindow(g_hWnd, nCmdShow);
//遊戲初始化
GameInit();
//消息循環
MSG msg = {};
while (WM_QUIT != msg.message)
{
//下面的代碼表示,一旦有消息就調用消息處理函數來處理消息
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//當沒有消息且窗口激活那麼就執行遊戲循環
else if (TRUE == g_Active)
{
//遊戲運行
GameRun();
}
//當沒有消息且窗口未激活就等待消息
else
WaitMessage();
}
//遊戲結束
GameEnd();
return (int)msg.wParam;
}
//消息回調函數
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_ACTIVATEAPP:
{
g_Active = static_cast<BOOL>(wParam);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(1);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//主顯示設備
HDC g_MainDC = nullptr;
//後備顯示設備
HDC g_BackDC = nullptr;
//正方形中心點位置
Vector2 rect_pos;
//正方形旋轉點位置
Vector2 pos[4];
//正方形速度
float rect_speed;
//角度變化值
float a;
//角度遞增量
float rotation;
struct Darts
{
//飛鏢的四點位置
Vector2 point[4];
//飛鏢的位置
Vector2 pos;
//角度變化值
float a;
//角度遞增量
float rotation;
//速度
Vector2 spd;
//是否丟出去了
bool b_throw;
bool b_return;
};
Darts darts[4];
void GameInit()
{
//得到主顯示設備
g_MainDC = GetDC(g_hWnd);
//創建後備顯示設備
g_BackDC = CreateCompatibleDC(g_MainDC);
//創建和主顯示設備兼容的位圖
HBITMAP hbmp = CreateCompatibleBitmap(g_MainDC, _CLIENT_PW, _CLIENT_PH);
//將兼容位圖選入兼容設備,刪除老的位圖
DeleteObject(SelectObject(g_BackDC, hbmp));
//初始化各項數據
rect_pos.Set(_CLIENT_PW / 2, _CLIENT_PH / 2);
rect_speed = 15.0f;
a = 0.0f;
rotation = 0.03f;
pos[0].Set(65, 0);
pos[1].Set(0, 65);
pos[2].Set(-65, 0);
pos[3].Set(0, -65);
for (int i = 0; i < 4; ++i)
{
darts[i].pos = pos[i];
darts[i].b_throw = false;
darts[i].b_return = false;
darts[i].a = 0.0f;
darts[i].rotation = 0.5f;
darts[i].point[0].Set(10, 0);
darts[i].point[1].Set(0, -10);
darts[i].point[2].Set(-10, 0);
darts[i].point[3].Set(0, 10);
darts[i].spd.Set(0, 0);
}
}
// 設備 起點x,y 終點x,y
void DrawLine(HDC hdc, float b_x1, float b_y1, float e_x2, float e_y2)
{
MoveToEx(hdc, b_x1, b_y1, nullptr);
LineTo(hdc, e_x2, e_y2);
}
void GameRun()
{
//得到從操作系統啓動一瞬間到目前的毫秒數
ULONGLONG begin_time = GetTickCount64();
//清屏
BitBlt(g_BackDC, 0, 0, _CLIENT_PW, _CLIENT_PH, nullptr, 0, 0, WHITENESS);
//畫正方形
Rectangle(g_BackDC, rect_pos.x - 40, rect_pos.y - 40, rect_pos.x + 40, rect_pos.y + 40);
//爲正方形四旋轉點創建變換矩陣
Matrix3 m_1[3];
m_1[0].Rotate(a += rotation);
m_1[1].Translate(rect_pos);
m_1[2] = m_1[0] * m_1[1];
//temp1得到的是:旋轉平移之後的新向量
Vector2 temp1[4];
for (int i = 0; i < 4; ++i)
{
temp1[i] = pos[i] * m_1[2];
}
//爲每個飛鏢創建變換矩陣
for (int i = 0; i < 4; ++i)
{
if (!darts[i].b_throw)
{
Matrix3 m_2[3];
m_2[0].Rotate(a += rotation);
m_2[1].Translate(temp1[i]);
m_2[2] = m_2[0] * m_2[1];
//temp2得到的是:旋轉平移之後的新向量
Vector2 temp2[4];
for (int j = 0; j < 4; ++j)
{
temp2[j] = darts[i].point[j] * m_2[2];
}
//畫飛鏢
DrawLine(g_BackDC, temp2[0].x, temp2[0].y, temp2[2].x, temp2[2].y);
DrawLine(g_BackDC, temp2[1].x, temp2[1].y, temp2[3].x, temp2[3].y);
}
else
{
Matrix3 m_3[3];
m_3[0].Rotate(a += rotation);
m_3[1].Translate(darts[i].pos);
m_3[2] = m_3[0] * m_3[1];
//temp3得到的是:旋轉平移之後的新向量
Vector2 temp3[4];
for (int j = 0; j < 4; ++j)
{
temp3[j] = darts[i].point[j] * m_3[2];
}
//畫飛鏢
DrawLine(g_BackDC, temp3[0].x, temp3[0].y, temp3[2].x, temp3[2].y);
DrawLine(g_BackDC, temp3[1].x, temp3[1].y, temp3[3].x, temp3[3].y);
darts[i].pos += darts[i].spd;
if (darts[i].pos.y < 0 && !darts[i].b_return)
{
darts[i].b_return = true;
}
if (darts[i].b_return)
{
darts[i].spd = temp1[i] - darts[i].pos;
darts[i].spd = darts[i].spd.Normalize() * 10.0f;
}
if (sqrt((float)(darts[i].pos.x - temp1[i].x) * (darts[i].pos.x - temp1[i].x) + (float)(darts[i].pos.y - temp1[i].y) * (darts[i].pos.y - temp1[i].y)) < 5.0f && darts[i].b_return)
{
darts[i].b_throw = false;
darts[i].b_return = false;
}
}
}
//移動
if (GetAsyncKeyState(VK_UP) & 0x8000)
{
rect_pos.y -= rect_speed;
}
else if (GetAsyncKeyState(VK_DOWN) & 0x8000)
{
rect_pos.y += rect_speed;
}
if (GetAsyncKeyState(VK_LEFT) & 0x8000)
{
rect_pos.x -= rect_speed;
}
else if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
{
rect_pos.x += rect_speed;
}
//扔飛鏢
if (GetAsyncKeyState(VK_SPACE) & 1)
{
for (int i = 0; i < 4; ++i)
{
if (!darts[i].b_throw)
{
darts[i].b_throw = true;
darts[i].spd.Set(0, -10.0f);
darts[i].pos = temp1[i];
darts[i].pos += darts[i].spd;
break;
}
}
}
//後備顯示設備顏色傳輸給主顯示設備
BitBlt(
g_MainDC,
0, 0, _CLIENT_PW, _CLIENT_PH,
g_BackDC,
0, 0,
SRCCOPY);
//得到本次遊戲循環總的消耗時間
ULONGLONG frame_time = GetTickCount() - begin_time;
//進行本次遊戲循環休息
if (_FRAME_SLEEPTIME > frame_time)
Sleep(_FRAME_SLEEPTIME - frame_time);
else
Sleep(1); //必須休息一下
}
void GameEnd()
{
//刪除後備設備,釋放主設備
DeleteDC(g_BackDC);
ReleaseDC(g_hWnd, g_MainDC);
}