C++游戲開發(2)

直接上代碼

1.首先是頭文件編寫

#include <iostream>
#include <graphics.h>

#include <string>

2,添加畫布

長1280,寬720

initgraph(1280, 720);

?3.添加主循環

?bool running = true;

while(runing)

{

????????

}

?4.定義結構體變量msg

ExMessge msg;

5.開啟渲染緩沖區

?BeginBatchDraw();//開啟第二張畫布

?cleardevice();//清屏
?FlushBatchDraw();//在第二張畫布里畫畫

?EndBatchDraw();//切換畫布?

6.按鍵或鼠標移動檢測

peekmessage是WINDOWS自帶的API函數,它可以檢測外部輸入設備的狀態,比如你鼠標動一下,鍵盤按一下,它都能檢測得到,然后將信號賦值給第一個參數結構體。

?while (peekmessage(&msg))//循環檢測按鍵或者鼠標移動
?{

?}

7.減少內存占用

DWORD start_time = GetTickCount();//記錄下開始的時間

DWORD end_time = GetTickCount();//記錄下結束的時間

DWORD delta_time = end_time - start_time;//記錄下循環一次的時間
if (delta_time < 1000 / 144)//用來實現幀數,為1s投放144張畫面,計算差值,用來彌補時間
{
? ? Sleep(1000 / 114 - delta_time);
}

然后是總的初始狀態設置。


#include <iostream>
#include <graphics.h>
int main()
{initgraph(1280, 720);bool running = true;ExMessage msg;BeginBatchDraw();//開啟第二張畫布while (running){DWORD start_time = GetTickCount();while (peekmessage(&msg)){}cleardevice();//清屏FlushBatchDraw();//在第二張畫布里畫畫DWORD end_time = GetTickCount();DWORD delta_time = end_time - start_time;if (delta_time < 1000 / 144){Sleep(1000 / 114 - delta_time);}}EndBatchDraw();//切換畫布return 0;
}

畫圖在哪里畫?

一定要放在清屏函數后,和調用畫布前。

正式開始項目

1.加載資源。將素材圖片放到項目的工程目錄下面,這里也是項目的根目錄

2.放置圖片,這個函數放在clear的后面,開啟第二張畫布的前面

?IMAGE img_background;//定義圖片

loadimage(&img_background, _T("img/background.png"));//加載圖片

第一個參數是圖片的地址,第二個是圖片在工程絕對路徑,以工程路徑為根路徑,在img文件下。

putimage(0, 0, &img_background);//放置圖片

如何讓動畫動起來?

角色動畫分為兩類,一種是序列幀動畫,一種是關鍵幀動畫

序列幀動畫由一組圖片構成,通過不斷切換動畫來實現動起來,借助視覺暫留效應。

關鍵幀動畫就是通過幾個點動來襯托整個物體動起來。

首先是初始化

int idx_current_anim;//用來記錄索引,實現循環播放一組圖片。
const int PLAYER_ANIM_NUM = 6;//記錄一組圖片的最大值

IMAGE img_player_left[PLAYER_ANIM_NUM];//用來記錄一組圖片

加載圖片,告訴編譯器圖片的位置。 wstring 是拼接函數。loadimage是加載圖片的函數。

void LoadAnimation()
{for (size_t i = 0; i < PLAYER_ANIM_NUM; i++){wstring path = L"img/player_left_" + to_wstring(i) + L".png";loadimage(&img_player_left[i], path.c_str());}
}

static int counter = 0;//記錄游戲幀
if(++counter%5==0)//實現執行5次循環就切換下一張圖片
idx_current_anim++;

?//循環播放操作,PLAYER_ANIM_NUM=6,idx_current_anim=6時,將其賦值為0,相當于重新賦值為0,和if -else類似。
?idx_current_anim = idx_current_anim % PLAYER_ANIM_NUM;

等價于

? ? ? ? if (idx_current_anim == 6)
? ? ? ? {
? ? ? ? ? ? idx_current_anim = 0;
? ? ? ? }

初始化加載

記住,初始化動畫加載函數后一定要放在主函數里面調用!?

動畫周圍正方形多余部分

圖中,動畫圖與背景圖格格不入。因為人物周圍的白框太出戲了。

這里引用的是博主“晚晶”的代碼。文章鏈接如下:

EasyX圖片透明度

void putpicture(int dstx, int dsty, IMAGE* img, COLORREF color, int alpha) {//0~255 255表示不透明
?? ?DWORD* imgp = GetImageBuffer(img);
?? ?DWORD* bgimgp = GetImageBuffer();
?? ?int w, bw, h, i, j;
?? ?w = img->getwidth();
?? ?bw = getwidth();
?? ?h = img->getheight();
?? ?color += 0xff000000;
?? ?if (alpha < 0)alpha = 0;
?? ?else if (alpha > 255)alpha = 255;
?? ?for (i = 0; i < h; ++i)
?? ??? ?for (j = 0; j < w; ++j)
?? ??? ??? ?if (imgp[i * w + j] != color)
?? ??? ??? ??? ?bgimgp[(i + dsty) * bw + j + dstx] = RGB(
?? ??? ??? ??? ??? ?((int)(alpha / 255.0 * GetRValue(imgp[i * w + j]) + (1 - alpha / 255.0) * GetRValue(bgimgp[(i + dsty) * bw + j + dstx]))),
?? ??? ??? ??? ??? ?((int)(alpha / 255.0 * GetGValue(imgp[i * w + j]) + (1 - alpha / 255.0) * GetGValue(bgimgp[(i + dsty) * bw + j + dstx]))),
?? ??? ??? ??? ??? ?((int)(alpha / 255.0 * GetBValue(imgp[i * w + j]) + (1 - alpha / 255.0) * GetBValue(bgimgp[(i + dsty) * bw + j + dstx])))
?? ??? ??? ??? ?);
}

第一個參數是x,第二個參數是y坐標,第三個參數圖片應用地址,第四個參數是RGB值。第五個是透明度大小(0-255,255是完全不透明)

將這段代碼進行調用,然后用putimage展示背景圖,博主代碼用來實現透明度圖片,這里的背景圖片最好是黑色更好,白色不好搞。

?? ??? ?putimage(0, 0, &img_background);//放置圖片
?? ??? ?for (int toumdu = 0; toumdu < 255; toumdu++)
?? ??? ?{
?? ??? ??? ?putpicture(100, 50, &img_player[idx_current_anim], RGB(255, 255, 255), toumdu);//放置圖片
?? ??? ?}
?? ??? ?

第二種方法:

//設置透明度
#pragma comment(lib,"MSIMG32.LIB")
inline void putimage_alpha(int x, int y, IMAGE* img)
{
? ? int w = img->getwidth();
? ? int h = img->getheight();
? ? AlphaBlend(GetImageHDC(NULL), x, y, w, h, GetImageHDC(img), 0, 0, w, h, { AC_SRC_OVER,0,255,AC_SRC_ALPHA });
}

?putimage_alpha(500, 500, &img_player_left[idx_current_anim]);

直接使用這個作為渲染

控制角色移動

首先創建一個角色初始位置。

POINT player_pos = { 500,500 };?

移動情況

? ? ? ? ? ? if (msg.message == WM_KEYDOWN)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? switch (msg.vkcode)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? case VK_UP:
? ? ? ? ? ? ? ? ? ? player_pos.y -= PLAYER_SPEED; break;
? ? ? ? ? ? ? ? case VK_DOWN:
? ? ? ? ? ? ? ? ? ? player_pos.y += PLAYER_SPEED; break;
? ? ? ? ? ? ? ? case VK_LEFT:
? ? ? ? ? ? ? ? ? ? player_pos.x -= PLAYER_SPEED; break;
? ? ? ? ? ? ? ? case VK_RIGHT:
? ? ? ? ? ? ? ? ? ? player_pos.x += PLAYER_SPEED; break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }

定義移動速度

int PLAYER_SPEED=10;//玩家速度,其實就是一次位置移動大小

設置邊界,只能在指定范圍內移動,超過則原地不動

y的范圍是30<y<600? ? ? ? ? ? ? ??

if (player_pos.y > 600)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? player_pos.y = 600;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (player_pos.y < 30)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? player_pos.y = 30;
? ? ? ? ? ? ? ? }

x的范圍是30<x<1200
? ? ? ? ? ? ? ? if (player_pos.x > 1200)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? player_pos.x = 1200;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (player_pos.x < 30)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? player_pos.x = 30;
? ? ? ? ? ? ? ? }

移動頓挫感

每當我們長按一個移動鍵移動角色的過程中,角色會先動一下,然后,再連續移動。這是因為我們按下一個鍵后,WIN會記下一個消息,但如果是連續按下的話,就會等待一段時間后才連續記錄消息。這是系統的原因,比如我們在打字過程中,長按一個j鍵,也是先顯示一個j,然后才突然顯示很多個j

解決辦法

改變游戲人物的移動邏輯,之前是按下哪個鍵,就怎樣怎樣,松開之后就不執行了,只是檢測有沒有按下。

現在的邏輯是:按下某個鍵,移動,松開某個鍵,停止移動,同時檢測按鍵的按下,松開

設置4個bool狀態,分別表示上下左右,初始值為false

?? ?bool is_move_up = false;
?? ?bool is_move_down = false;
?? ?bool is_move_left = false;
?? ?bool is_move_right = false;

?按鍵循環采集采用if -if

while (peekmessage(&msg))//獲取一個消息
{
?? ?//按鍵被按下
?? ?if (msg.message == WM_KEYDOWN)
?? ?{
?? ??? ?cout << "報告!按鍵被按下,請求指示!" << endl;
?? ??? ?switch (msg.vkcode)
?? ??? ?{
?? ??? ?case 0x57:is_move_up=true; cout << "向上移動" << endl;; break;
?? ??? ?case 0x53:is_move_down=true; cout << "向下移動" << endl; break;
?? ??? ?case 0x44:is_move_right =true; cout << "向右移動" << endl; break;
?? ??? ?case 0x41:is_move_left=true; cout << "向左移動" << endl; break;
?? ??? ?}
?? ?}
?? ?else if (msg.message == WM_KEYUP)
?? ?{
?? ??? ?cout << "按鍵彈起,請求指示!" << endl;
?? ??? ?switch (msg.vkcode)
?? ??? ?{
?? ??? ?case 0x57:is_move_up = false; cout << "向上停止" << endl;; break;
?? ??? ?case 0x53:is_move_down = false; cout << "向下停止" << endl; break;
?? ??? ?case 0x44:is_move_right = false; cout << "向右停止" << endl; break;
?? ??? ?case 0x41:is_move_left = false; cout << "向左停止" << endl; break;
?? ??? ?}
?? ?}

然后再設置true的情況

/*野豬移動狀態函數塊*/
{
?? ?if (is_move_up) player_pos.y -= MOTION_SPEED;
?? ?if (is_move_down) player_pos.y += MOTION_SPEED;
?? ?if (is_move_right) player_pos.x += MOTION_SPEED;
?? ?if (is_move_left) player_pos.x -= MOTION_SPEED;
?? ?putimage_alpha(player_pos.x, player_pos.y, &ENEMY_LEFT[idx_current_anim]);
}

關于沖突按鍵ctrl

按下W S A 會間接觸發“按下ctrl鍵”,所以在使用按鍵定義時一定要注意,不要在ctrl判斷里面加循環,或者system("pause"),system("cls")。

面向對象編程

首先引入vector容器

創建類和容器

class Animation
{
public:Animation(){}~Animation(){}
private:int interval_ms=0;vector<IMAGE*>frame_list;
};

把加載圖片資源的部分放在構造 函數里面

編寫加載圖片的函數在構造函數里面

	Animation(LPCTSTR path,int num,int interval){interval_ms = interval;TCHAR path_file[256];for (size_t i = 0; i < num; i++){_stprintf_s(path_file, path, i);IMAGE* frame = new IMAGE();loadimage(frame, path_file);frame_list.push_back(frame);//添加到vector容器中}}

記住要釋放內存。在析構函數中實現

for (size_t i = 0; i < frame_list.size(); i++)delete frame_list[i];//釋放vectro容器的所有值

編寫幀索引更新和渲染的代碼

用計數器和計時器來控制動畫的幀更新有什么區別?

計數器的幀更新略微不穩定有時候1幀的時間長,有時短,但是計時器控制的幀是肯定比計數器穩定

void Play(int x, int y, int delta)
{timer += delta;if (timer >= interval_ms){idx_frame = (idx_frame + 1) % frame_list.size();timer = 0;}
}在private中定義
private:int idx_frame = 0;//動畫幀索引int timer = 0;//動畫計時器

初始化

	Animation anim_left_player(_T("img/player_left_%d.png"), 6, 45);//初始化左邊圖片Animation anim_right_player(_T("img/player_right_%d.png"), 6, 45);//初始化右邊圖片

翻轉角色

void DrawPlayer(int delta, int dir_x)
{static bool facing_left = false;if (dir_x < 0)facing_left = true;else if (dir_x > 0)facing_left = false;if (facing_left)anim_left_player.Play(player_pos.x, player_pos.y, delta);else anim_right_player.Play(player_pos.x, player_pos.y, delta);
}

添加陰影

IMAGE img_shadow;//初始化玩家腳下的陰影
loadimage(&img_shadow, _T("img/shadow_player.png"));

定義陰影三度

const int PLAYER_WIDTH = 80;//玩家寬度
const int PLAYER_HEIGHT = 80;//玩家高度
const int SHADOW_WIDTH = 32;//陰影寬度

計算陰影坐標,代碼放到翻轉函數中

	int pos_shadow_x = player_pos.x + (PLAYER_WIDTH / 2 - SHADOW_WIDTH / 2);//計算陰影的xint pos_shadow_y = player_pos.y + PLAYER_HEIGHT - 8;//計算陰影的y
putimage_alpha(pos_shadow_x, pos_shadow_y, &img_shadow);

然后開始測試

把代碼放進循環中

DrawPlayer(1000 / 100, Go_right-Go_left);

可以發現一個問題,就是玩家在走斜線時移動速度比左右上下快不少

使用代碼

		//防止往斜上方走速度過快int dir_x = Go_right - Go_left;int dir_y = Go_down-Go_up;double len_dir = sqrt(dir_x * dir_x + dir_y * dir_y);if (len_dir != 0){double normalized_x = dir_x / len_dir;double normalized_y = dir_y / len_dir;player_pos.x += (int)(Player_speed * normalized_x);player_pos.y += (int)(Player_speed * normalized_y);}

限制玩家區域

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916982.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916982.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916982.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Unity開發2D類銀河惡魔城游戲學習筆記目錄

Unity開發2D類銀河惡魔城游戲學習筆記 Unity教程&#xff08;零&#xff09;Unity和VS的使用相關內容 玩家狀態機 Unity教程&#xff08;一&#xff09;開始學習狀態機 Unity教程&#xff08;二&#xff09;角色移動的實現 Unity教程&#xff08;三&#xff09;角色跳躍的實現…

智慧社區項目開發(三)——基于 Spring Boot 實現動態路由加載:從數據庫到前端菜單的完整方案

在后臺管理系統中&#xff0c;不同用戶角色往往擁有不同的操作權限&#xff0c;對應的菜單展示也需動態調整。動態路由加載正是解決這一問題的核心方案 —— 根據登錄用戶的權限&#xff0c;從數據庫查詢其可訪問的菜單&#xff0c;封裝成前端所需的路由結構并返回。本文將詳細…

Python在自動化與運維領域的核心角色:工具化、平臺化與智能化

&#x1f4dd;個人主頁&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的關注 &#x1f339;&#x1f339; 引言 在 IT 系統日益復雜、運維任務持續增長的今天&#xff0c;自動化已成為企業基礎設施管理的關鍵方向。Python 以其簡潔的語法、強大…

RAG實戰指南 Day 28:RAG系統緩存與性能優化

【RAG實戰指南 Day 28】RAG系統緩存與性能優化 開篇 歡迎來到"RAG實戰指南"系列的第28天&#xff01;今天我們將深入探討RAG系統的緩存機制與性能優化策略。在實際生產環境中&#xff0c;RAG系統往往面臨高并發、低延遲的需求&#xff0c;而合理的緩存設計和性能優…

swanlab實驗優雅起名

init中的參數的作用project&#xff1a;整個實驗的名字&#xff1b;experiment_name&#xff1a;在這個實驗中&#xff0c;你的名字是什么&#xff1b; 比如說現在我們要進行對比實驗&#xff0c;PEAN和Triflownet分別是對比方法的名字&#xff0c;這樣的好處是&#xff0c;她們…

Nestjs框架: NestJS 核心機制解析 —— DI(依賴注入)容器與模塊化工作原理

理解 NestJS 的 DI 管理機制 我們想要了解依賴注入&#xff08;Dependency Injection, DI&#xff09;最核心的工作邏輯NestJS 擁有自己的一套 DI 管理系統&#xff0c;它通過一個稱為 DI 容器 的機制&#xff0c;來統一管理應用中所有類&#xff08;class&#xff09;的依賴關…

日語學習-日語知識點小記-構建基礎-JLPT-N3階段(12):文法+單詞

日語學習-日語知識點小記-構建基礎-JLPT-N3階段&#xff08;12&#xff09;&#xff1a;文法單詞 1、前言&#xff08;1&#xff09;情況說明&#xff08;2&#xff09;工程師的信仰2、知識點&#xff11;ーたぶん 多分&#xff12;ーV&#xff08;て&#xff09;いく ? V&…

【趙渝強老師】OceanBase租戶的資源管理

OceanBase數據庫是多租戶的數據庫系統&#xff0c;一個集群內可包含多個相互獨立的租戶&#xff0c;每個租戶提供獨立的數據庫服務。在OceanBase數據庫中&#xff0c;使用資源配置&#xff08;Unit Config&#xff09;、資源單元&#xff08;Unit&#xff09;和資源池&#xff…

8K、AI、低空智聯,H.266能否撐起下一代視頻通路?

一、&#x1f4c8; 爆發式增長的 AI 與視頻數據&#xff1a;智能時代的“數據燃料革命” 隨著生成式 AI、大模型推理、多模態理解等技術的迅猛發展&#xff0c;視頻數據從“記錄工具”轉變為“感知基礎設施”&#xff0c;其在現代智能系統中的戰略地位日益凸顯。 1?? 視頻數…

保姆級別IDEA關聯數據庫方式、在IDEA中進行數據庫的可視化操作(包含圖解過程)

本文以mysql為例&#xff0c;學會了Mysql&#xff0c;其它的數據庫也是類似的模版~如果您覺得這邊文章對你有幫助&#xff0c;可以收藏防止找不到~如果您覺得這篇文章不錯&#xff0c;也感謝您的點贊對我創作的支持1.1 打開側邊欄的Database2.2 選擇要連接的數據庫&#xff08;…

33.【.NET8 實戰--孢子記賬--從單體到微服務--轉向微服務】--單體轉微服務--財務服務--記賬

這篇文章我們一起把記賬模塊從單體應用遷移到微服務架構中。記賬模塊的功能想必大家都已經了解了&#xff0c;主要是記錄用戶的收入和支出&#xff0c;以及對這些記錄的刪除修改和查詢等操作。具體的功能可以參考單體應用專欄&#xff0c;在這里就不多講了。我們現在一起開始遷…

Cursor結合Playwright MCP Server支持自動化

Cursor結合Playwright MCP Server支持自動化 今天分享一下 playwright MCP Server&#xff0c;其提供了瀏覽器自動化能力&#xff0c;使大型語言模型能夠在真實的瀏覽器環境中與網頁交互&#xff0c; 也可以執行任務&#xff0c;例如運行JavaScript、截屏和導航網頁元素&…

Python 求梯形面積的程序(Program to find area of a Trapezoid)

梯形的定義&#xff1a; 梯形是凸四邊形&#xff0c;至少有一對邊平行。平行邊稱為梯形的底邊&#xff0c;另外兩條不平行的邊稱為梯形的腿。梯形也可以有兩對底邊。在上圖中&#xff0c;CD || AB&#xff0c;它們構成底邊&#xff0c;而另外兩條邊&#xff0c;即AD和BC&#…

C語言 —— 指針(4)

動態內存分配動態內存需要手動申請&#xff0c;手動歸還&#xff0c;其內存是開辟在堆區。申請的函數為&#xff1a;void *malloc(size_t size) &#xff08;需包含頭文件#include<stdlib.h>&#xff09;size&#xff1a;要分配的內存大小&#xff0c;以字節為單位。申請…

常用算法思想及模板

今天繼續整理一些關于算法競賽中C適用的一些模板以及思想。 保留x位小數 保留x位小數在C語言中可以使用printf中的"%.xf"來實現&#xff0c;但是很多C選手由于關閉了同步流&#xff0c;害怕cin、cout與scanf、printf混用容易出錯&#xff0c;所以就給大家介紹一個強…

GitLab 倉庫 — 常用的 git 命令

在公司的 gitlab 公共倉庫中寫代碼做項目時&#xff0c;主要涉及以下常用 git 命令&#xff1a;一、單個命令講解1. 拉取代碼&#xff08;1&#xff09;git clone [倉庫 URL]?克隆遠程倉庫到本地&#xff08;需確保 URL 正確&#xff09; ?&#xff08;?2&#xff09;git pu…

【28】C# WinForm入門到精通 ——多文檔窗體MDI【屬性、方法、實例、源碼】【多窗口重疊、水平平鋪、垂直平鋪、窗體傳值】

文章目錄1多文檔窗體MDI2 基本設置3 實例&#xff1a;多窗口重疊、水平平鋪、垂直平鋪3.1 主窗口屬性設置3.2 主窗口3.3 主窗口窗口添加MenuStrip菜單3.4 添加處理函數3.5 測試效果4 利用窗體參數定義進行傳值4.1 在Form2、Form3添加相關控件4.2 Form3 定義函數public Form3(st…

【計算機科學與應用】基于Session欺騙攻擊的Web應用程序防護

導讀&#xff1a; 本文對Web應用程序開發中的Session欺騙攻擊進行了闡述&#xff0c;詳細講解了防范Session欺騙攻擊的三種傳統方法&#xff0c;并給出了防范代碼&#xff0c;分析了三種傳統防范方法的不足&#xff0c;新設計了一種通過Referer信息驗證來加強對Session欺騙的防…

yolo8+阿里千問圖片理解(華為簡易版小藝看世界)

? 實現目標 按下空格鍵 → 獲取攝像頭當前畫面&#xff1b; 將圖片上傳給 大模型 接口&#xff0c;讓其“看圖說話”&#xff1b; 獲取返回描述后&#xff0c;以字幕形式展示在圖像畫面上&#xff1b; 持續顯示識別結果&#xff0c;直到下次按空格。 &#x1f9e0; 需要準…

【ee類保研面試】數學類---線性代數

25保研er&#xff0c;希望將自己的面試復習分享出來&#xff0c;供大家參考 part0—英語類 part1—通信類 part2—信號類 part3—高數類 part100—self項目準備 文章目錄線性代數知識點大全**1. 余子式與代數余子式****2. 行列式的含義****3. 矩陣的秩&#xff08;Rank&#xf…