簡單渲染流水管線C++代碼實現(三)---實戰之《忍者飛鏢》

今天的主題:忍者飛鏢(仍然利用自己寫的向量和矩陣實現)
介紹:飛鏢丟出後能夠返回原來的位置

注意點:
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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章