今天的主题:忍者飞镖(仍然利用自己写的向量和矩阵实现)
介绍:飞镖丢出后能够返回原来的位置
注意点:
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);
}