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