網(wǎng)上有一個香蕉金剛的跑酷游戲,不過我們這個扔香蕉游戲模仿的并不是這個,而是模仿的微軟在 20 多年前的一個小游戲,不知道誰也有印象呢?
圖片都是從原來的游戲中抓圖弄出來的,顏色也是從原游戲抓圖中取色設置的,應該和原來的風格很像。
你的任務是用香蕉擊中你的對手。
你可以通過鼠標調(diào)整投擲香蕉的角度和力度,香蕉會受重力加速度的影響。同時,請注意屏幕底部表示風力的箭頭,香蕉同樣會受風力影響。風力的箭頭越長,表示風力越強。還有,周圍的樓宇會阻擋你的香蕉。(好像有點像憤怒的小鳥)
游戲中涉及到兩個玩家的代表人物和香蕉們,你可能需要自己找到兩張圖,然后通過easyx的貼圖技術弄進去。當然你也可以來找我(在文末)
其他的部分你可以直接查看下面的游戲源代碼:
本項目編譯環(huán)境:Visual Studio 2013/2019/2022,EasyX插件
代碼展示:
1.定義變量、函數(shù)和一些必要的常量
#include#include #include #include #include // 定義常量 #define PI 3.1415926536 // 圓周率 #define SCRWIDTH 640 // 屏幕寬度 #define SCRHEIGHT 480 // 屏幕高度 #define GRAVITY 9.8 // 重力加速度 #define BACKATTR BLUE // 背景的顏色 #define OBJECTCOLOR 0x55AAFF // 對手的顏色 #define EXPLOSIONCOLOR 0x5500FF // 爆炸的顏色 #define SUNATTR 0x00FFFF // 太陽的顏色 #define SUNHEIGHT 40 // 太陽的高度 #define SUNHAPPY true // 太陽高興 #define SUNSHOCK false // 太陽受驚 // 全局變量 IMAGE g_imgBanana[4]; // 香蕉圖片 IMAGE g_imgGorD; // 大猩猩(雙手放下) IMAGE g_imgGorL; // 大猩猩(左邊的手抬起) IMAGE g_imgGorR; // 大猩猩(右邊的手抬起) POINT g_ptGorilla[2]; // 兩個游戲者的位置 int g_iLastBuilding; // 最后一棟樓的編號 int g_iWind; // 風力 bool g_bSunHit; // 是否擊中太陽 // 函數(shù)定義 void Init(); // 初始化 void Intro(); // 游戲介紹 void PlayGame(TCHAR *player1, TCHAR *player2); // 主游戲函數(shù) void MakeCityScape(POINT *aryBCoor); // 創(chuàng)建隨機的游戲場景 void PlaceGorillas (POINT *aryBCoor); // 將游戲者放到樓宇頂端 void DoSun(bool smile); // 繪制太陽 bool DoShot(int idPlayer, int x, int y, int* win); // 接收游戲者輸入,實現(xiàn)扔香蕉攻擊對方 int PlotShot(int startX, int startY, double angle, int velocity, int idPlayer); // 進行香蕉攻擊,使香蕉劃過屏幕 void DrawBanana(int x, int y, int r, bool d); // 繪制香蕉 void DoExplosion(int x, int y); // 香蕉攻擊后的爆炸效果 int ExplodeGorilla(int x, int y); // 游戲者死亡后爆炸 void VictoryDance(int idPlayer); // 繪制跳舞的大猩猩(勝利后執(zhí)行)
2.初始化游戲圖片元素(香蕉和猩猩本猩)
void Init()
{
initgraph(SCRWIDTH, SCRHEIGHT); // 創(chuàng)建繪圖窗口
srand((unsigned int)time(NULL)); // 設置隨機種子
// 初始化香蕉圖案
IMAGE tmp;
loadimage(&tmp, _T("res\Banana.gif"));
SetWorkingImage(&tmp);
getimage(&g_imgBanana[0], 0, 0, 9, 7);
getimage(&g_imgBanana[1], 9, 0, 9, 7);
getimage(&g_imgBanana[2], 18, 0, 9, 7);
getimage(&g_imgBanana[3], 27, 0, 9, 7);
// 初始化大猩猩圖案
loadimage(&tmp, _T("res\Gorilla.gif"), 0, 0, true);
SetWorkingImage(&tmp);
getimage(&g_imgGorD, 0, 0, 30, 30);
getimage(&g_imgGorL, 30, 0, 30, 30);
getimage(&g_imgGorR, 60, 0, 30, 30);
SetWorkingImage(NULL);
}
3.游戲的簡單介紹
void Intro()
{
settextstyle(24, 0, _T("宋體"));
// 在屏幕中央輸出字符串
RECT r = {0, 40, 640, 80};
drawtext(_T("扔香蕉的大猩猩"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
settextstyle(16, 0, _T("System"));
r.top = 120;
r.bottom = 480;
drawtext(_T("這個游戲模仿的微軟在 20 多年前的一個小游戲,
不知道誰也有印象呢?
")
_T("你的任務是用香蕉擊中你的對手。
你可以通過鼠標調(diào)整投擲香蕉的角度和力度,
")
_T("香蕉會受重力加速度的影響。
同時,請注意屏幕底部表示風力的箭頭,")
_T("香蕉同樣會受風力影響。
風力的箭頭越長,表示風力越強。
")
_T("還有,周圍的樓宇會阻擋你的香蕉。
"),
&r, DT_CENTER | DT_VCENTER);
r.top = 400;
drawtext(_T("按任意鍵繼續(xù)"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
getmessage(EM_CHAR);
}
4.主游戲函數(shù)
// 參數(shù):
// player1, player2:游戲者名稱
void PlayGame(TCHAR *player1, TCHAR *player2)
{
POINT aryBCoor[31]; // 樓宇群的坐標
int aryScore[2] = {0, 0}; // 兩個游戲者的得分
TCHAR sScore[20]; // 保存得分的字符串
int player = 0; // 攻擊者
setbkcolor(BACKATTR);
while(true)
{
cleardevice();
MakeCityScape(aryBCoor);
PlaceGorillas(aryBCoor);
DoSun(SUNHAPPY);
bool bHit = false;
while(bHit == false)
{
settextcolor(WHITE);
RECT r = {0, 0, SCRWIDTH, 20};
drawtext(player1, &r, DT_LEFT | DT_SINGLELINE);
drawtext(player2, &r, DT_RIGHT | DT_SINGLELINE);
r.top = SCRHEIGHT - 40;
r.bottom = SCRHEIGHT - 20;
#if _MSC_VER > 1200
_stprintf_s(sScore, _T("%d >Score< %d"), aryScore[0], aryScore[1]);
#else
_stprintf(sScore, _T("%d >Score< %d"), aryScore[0], aryScore[1]);
#endif
drawtext(sScore, &r, DT_CENTER | DT_SINGLELINE);
int win;
// 進行攻擊。擊中任意游戲者即返回 true。同時,更新 win 為勝利者
bHit = DoShot(player, g_ptGorilla[player].x, g_ptGorilla[player].y, &win);
// 如果太陽被擊中,重繪太陽
if (g_bSunHit) DoSun(SUNHAPPY);
// 如果擊中對手,更新分數(shù)
if (bHit == true) aryScore[win]++;
// 交替攻擊
player = 1 - player;
Sleep(100);
}
Sleep(1000);
};
}
5.創(chuàng)建隨機的游戲場景
// 參數(shù):
// aryBCoor[]:存儲每一棟樓的左上角坐標
void MakeCityScape(POINT *aryBCoor)
{
int x = -10;
// 設置隨機的樓群傾斜的趨勢
int slope = rand() % 6;
int iNewHt; // 新樓的高度
switch(slope)
{
case 0: iNewHt = 15; break; // 逐漸升高
case 1: iNewHt = 130; break; // 逐漸降低
case 2:
case 3:
case 4: iNewHt = 15; break; // 倒 "V" 型(比較常見)
case 5: iNewHt = 130; break; // "V" 型
}
int iBottomLine = 465; // 建筑的最低端
int iHtInc = 10; // 高度增加值
int iDefBWidth = 37; // 默認的建筑寬度
int iRandomHeight = 120; // 隨機的高度差異
int iWWidth = 3; // 窗戶寬度
int iWHeight = 6; // 窗戶高度
int iWDifV = 15; // 窗戶的垂直間距
int iWDifH = 10; // 窗戶的水平間距
int iCurBuilding = 0;
do
{
switch(slope)
{
case 0: iNewHt += iHtInc; break;
case 1: iNewHt -= iHtInc; break;
case 2:
case 3:
case 4: if (x > SCRWIDTH / 2) iNewHt -= 2 * iHtInc;
else iNewHt += 2 * iHtInc;
break;
case 5: if (x > SCRWIDTH / 2) iNewHt += 2 * iHtInc;
else iNewHt -= 2 * iHtInc;
break;
}
// 設置樓宇寬度,并檢查是否超出屏幕
int iBWidth = iDefBWidth + rand() % iDefBWidth;
// 設置樓宇高度,并檢查樓宇是否超出屏幕下方
int iBHeight = iNewHt + rand() % iRandomHeight;
if (iBHeight < iHtInc)
iBHeight = iHtInc;
// 檢查樓宇是否太高
if (iBottomLine - iBHeight <= 25)
iBHeight = 20;
// 保存樓的坐標
aryBCoor[iCurBuilding].x = x;
aryBCoor[iCurBuilding].y = iBottomLine - iBHeight;
// 繪制樓宇
COLORREF aryBuildingColor[3] = {CYAN, LIGHTGRAY, RED}; // 定義樓宇的三種顏色
int colorID = rand() % 3;
setlinecolor(BACKATTR);
rectangle(x - 1, iBottomLine + 1, x + iBWidth + 1, iBottomLine - iBHeight - 1);
setfillcolor(aryBuildingColor[colorID]);
solidrectangle(x, iBottomLine, x + iBWidth, iBottomLine - iBHeight);
// 繪制窗戶
int c = x + 3;
do
{
for(int i = iBHeight - 3; i >= 7; i -= iWDifV)
{
int winColor;
if (rand() % 4 == 0)
winColor = DARKGRAY;
else
winColor = YELLOW;
setfillcolor(winColor);
solidrectangle(c, iBottomLine - i, c + iWWidth, iBottomLine - i + iWHeight);
}
c += iWDifH;
}
while(c < x + iBWidth - 3);
x += iBWidth + 2;
iCurBuilding++;
}
while(x < SCRWIDTH - 1);
g_iLastBuilding = iCurBuilding - 1; // 保存最后一棟樓的編號
// 設置隨機風力
g_iWind = rand() % 61 - 30;
// 繪制風向箭頭
if (g_iWind != 0)
{
int windLine = g_iWind * 3 * (SCRWIDTH / 320);
setlinecolor(EXPLOSIONCOLOR);
int arrowDir = (g_iWind > 0) ? -2 : 2;
line(SCRWIDTH / 2, SCRHEIGHT - 5, SCRWIDTH / 2 + windLine, SCRHEIGHT - 5);
line(SCRWIDTH / 2 + windLine, SCRHEIGHT - 5, SCRWIDTH / 2 + windLine + arrowDir, SCRHEIGHT - 5 - 2);
line(SCRWIDTH / 2 + windLine, SCRHEIGHT - 5, SCRWIDTH / 2 + windLine + arrowDir, SCRHEIGHT - 5 + 2);
}
}
6.繪制游戲者和太陽的位置
// 將游戲者放到樓宇頂端(從邊緣數(shù)第二個或第三個樓宇上)
// 參數(shù):
// aryBCoor[]:樓宇數(shù)組。保存每棟樓的左上角坐標
void PlaceGorillas(POINT *aryBCoor)
{
for (int i = 0; i <= 1; i++)
{
int iBNum = (i == 0) ? rand() % 2 + 1 : g_iLastBuilding - 1 - rand() % 2;
int iBWidth = aryBCoor[iBNum + 1].x - aryBCoor[iBNum].x;
g_ptGorilla[i].x = aryBCoor[iBNum].x + iBWidth / 2 - g_imgGorD.getwidth() / 2;
g_ptGorilla[i].y = aryBCoor[iBNum].y - g_imgGorD.getheight();
putimage(g_ptGorilla[i].x, g_ptGorilla[i].y, &g_imgGorD);
}
}
// 繪制太陽
// 參數(shù):
// smile:太陽是否微笑
void DoSun(bool smile)
{
// 設置太陽的位置
int x = SCRWIDTH / 2;
int y = SUNHEIGHT - 15;
// 繪制太陽
// 臉
setlinecolor(SUNATTR);
setfillcolor(SUNATTR);
fillcircle(x, y, 12);
// 光芒
for (double a = 0; a < PI * 2; a += PI / 8)
line(x, y, int(x + cos(a) * 20 + 0.5), int(y + sin(a) * 16 + 0.5));
// 嘴
setlinecolor(BACKATTR);
setfillcolor(BACKATTR);
if (smile) // 繪制笑臉
arc(x - 8, y - 8, x + 8, y + 8, (210 * PI / 180), (330 * PI / 180));
else // 繪制受驚表情("o" 型嘴)
fillcircle(x, y + 5, 3);
// 眼睛
fillcircle(x - 3, y - 2, 1);
fillcircle(x + 3, y - 2, 1);
}
7.實現(xiàn)按鍵操作,實現(xiàn)扔香蕉功能
// 參數(shù):
// idPlayer:游戲者(準備扔香蕉的)
// x, y:游戲者的位置
bool DoShot(int idPlayer, int x, int y, int *win)
{
// 清空鼠標消息緩沖區(qū)
flushmessage(EM_MOUSE);
// 攻擊的起始位置
int startx = x + (idPlayer == 1 ? g_imgGorD.getwidth() : 0);
int starty = y;
// 角度輔助線的位置
int mx = startx, my = starty - 90;
int oldmx = mx, oldmy = my;
double angle = PI / 2; // 投擲角度
int velocity = 2; // 投擲力度
setrop2(R2_XORPEN);
setlinecolor(RED);
line(startx, starty, mx, my);
// 鼠標輸入攻擊角度
ExMessage msg;
while(true)
{
msg = getmessage(EM_MOUSE);
if (msg.message == WM_MOUSEMOVE)
{
if (msg.y > y)
{
mx = startx + (msg.x > startx ? 90 : -90);
my = starty;
angle = msg.x > startx ? 0 : PI;
}
else if (msg.x != startx)
{
angle = atan((double(starty) - msg.y) / (double(msg.x) - startx));
if (angle < 0) angle += PI;
mx = startx + int(cos(angle) * 90 + 0.5);
my = starty - int(sin(angle) * 90 + 0.5);
}
else
{
mx = msg.x;
my = y - 90;
angle = PI / 2;
}
line(startx, starty, oldmx, oldmy);
line(startx, starty, mx, my);
oldmx = mx;
oldmy = my;
}
else if (msg.message == WM_LBUTTONDOWN)
break;
}
line(startx, starty, oldmx, oldmy);
// 鼠標輸入攻擊力度
setlinestyle(PS_SOLID, 8);
oldmx = mx = startx + int(cos(angle) * velocity + 0.5);
oldmy = my = starty - int(sin(angle) * velocity + 0.5);
line(startx, starty, mx, my);
while(true)
{
if (peekmessage(&msg))
{
if (msg.message == WM_LBUTTONUP)
break;
}
mx = startx + int(cos(angle) * velocity + 0.5);
my = starty - int(sin(angle) * velocity + 0.5);
line(startx, starty, oldmx, oldmy);
line(startx, starty, mx, my);
oldmx = mx;
oldmy = my;
if (++velocity > 90) velocity = 2;
Sleep(20);
}
velocity *= 2; // 力度擴大一倍
line(startx, starty, oldmx, oldmy);
// 恢復設置
setlinestyle(PS_SOLID, 1);
setrop2(R2_COPYPEN);
// 實施攻擊
g_bSunHit = false;
int iPlayerHit = PlotShot(x, y, angle, velocity, idPlayer);
// 攻擊結果
if (iPlayerHit == -1)
{
*win = -1;
return false;
}
else
{
*win = (iPlayerHit == idPlayer) ? 1 - idPlayer : idPlayer;
VictoryDance(*win);
return true;
}
}
8.扔出香蕉,計算坐標,弧度等等
// 進行香蕉攻擊,使香蕉劃過屏幕
// 參數(shù):
// startX, startY:游戲者(扔香蕉的)的坐標
// angle:扔出的方向(弧度)
// velocity:扔出的力度
// idPlayer:游戲者(扔香蕉的)
int PlotShot(int startX, int startY, double angle, int velocity, int idPlayer)
{
// 投擲力量在 x、y 方向上的分量
double initXVel = cos(angle) * velocity;
double initYVel = sin(angle) * velocity;
double x, y;
double oldx = startX;
double oldy = startY;
// 繪制游戲者(投擲動作)
putimage(startX, startY, idPlayer == 0 ? &g_imgGorL : &g_imgGorR);
Sleep(100);
// 繪制游戲者(站立動作)
putimage(startX, startY, &g_imgGorD);
bool bImpact = false; // 是否碰撞
bool bShotInSun = false; // 是否擊中太陽
bool bOnScreen = true; // 香蕉是否在屏幕上
int iPlayerHit = -1; // 是否擊中對手(-1:未擊中;0、1:被擊中者的 ID)
bool bNeedErase = false; // 是否需要擦掉舊香蕉
POINT look[4]; // 碰撞檢測的位置(香蕉中心上下左右四個邊的中點)
look[2].x = 0; look[3].x = g_imgBanana[0].getwidth() - 1;
look[0].x = look[1].x = look[3].x / 2;
look[0].y = 0; look[1].y = g_imgBanana[0].getheight() - 1;
look[2].y = look[3].y = look[1].y / 2;
int startXPos = startX;
int startYPos = startY - g_imgBanana[0].getheight();
if (idPlayer == 1)
startXPos = startXPos + g_imgGorD.getwidth() - g_imgBanana[0].getwidth();
int pointColor = 0;
int rot;
double t = 0;
while(!bImpact && bOnScreen)
{
// 擦掉舊香蕉
if (bNeedErase)
{
bNeedErase = false;
DrawBanana(int(oldx + 0.5), int(oldy + 0.5), -1, false);
}
x = startXPos + (initXVel * t) + (g_iWind / 5.0 * t * t);
y = startYPos + (-1 * (initYVel * t) + (GRAVITY * t * t));
if ((x >= SCRWIDTH - 10.0) || (x <= 3) || (y >= SCRHEIGHT - 3.0))
bOnScreen = false;
if (bOnScreen && y > 0)
{
// 檢測是否擊中(對香蕉中心上下左右四個邊的中點做檢測)
for (int i = 0; i < 4; i ++)
{
pointColor = getpixel(int(x + look[i].x + 0.5), int(y + look[i].y + 0.5));
if (pointColor == BACKATTR || pointColor == WHITE) // 目標是背景色或白色字幕,未擊中
{
bImpact = false;
if (bShotInSun == true && (abs(SCRWIDTH / 2 - int(x)) > 20 || y > SUNHEIGHT))
bShotInSun = false;
}
else if (pointColor == SUNATTR && y < SUNHEIGHT) // 擊中太陽
{
if (!g_bSunHit)
DoSun(SUNSHOCK);
g_bSunHit = true;
bShotInSun = true;
}
else
bImpact = true;
if (bImpact)
break;
}
if (!bShotInSun && !bImpact)
{
// 繪制香蕉
rot = int(t * 10) % 4;
DrawBanana(int(x + 0.5), int(y + 0.5), rot, true);
bNeedErase = true;
}
oldx = x;
oldy = y;
}
t += 0.1;
Sleep(50);
}
if (pointColor != OBJECTCOLOR && bImpact)
DoExplosion(int(x + g_imgBanana[0].getwidth() / 2 + 0.5), int(y + g_imgBanana[0].getheight() / 2 + 0.5));
else if (pointColor == OBJECTCOLOR)
iPlayerHit = ExplodeGorilla(int(x + 0.5), int(y + 0.5));
return iPlayerHit;
}
9.當然還是不能忘記不斷對香蕉的位置進行刷新
// 參數(shù):
// x, y:香蕉的位置
// r:香蕉的旋轉位置
// d:繪制還是擦除(true:繪制;false:擦除)
void DrawBanana(int x, int y, int r, bool d)
{
static IMAGE oldimg;
if (d)
{
getimage(&oldimg, x, y, g_imgBanana[0].getwidth(), g_imgBanana[0].getheight());
putimage(x, y, &g_imgBanana[r]);
}
else
putimage(x, y, &oldimg);
}
10.實現(xiàn)香蕉命中后的爆炸效果以及角色死亡效果
// 香蕉攻擊后的爆炸效果
// 參數(shù):
// x, y:爆炸的位置
void DoExplosion(int x, int y)
{
int r = 10;
int i;
setlinecolor(EXPLOSIONCOLOR);
for (i = 0; i <= r; i++)
{
circle(x, y, i);
Sleep(16);
}
setlinecolor(BACKATTR);
for (i = r; i >= 0; i--)
{
circle(x, y, i);
Sleep(16);
}
setfillcolor(BACKATTR);
fillcircle(x, y, r);
}
// 游戲者死亡后爆炸
// 參數(shù):
// x, y:攻擊的位置
int ExplodeGorilla (int x, int y)
{
int iPlayerHit = (x < SCRWIDTH / 2) ? 0 : 1;
int iPlayerX = g_ptGorilla[iPlayerHit].x + g_imgGorD.getwidth() / 2;
int iPlayerY = g_ptGorilla[iPlayerHit].y + g_imgGorD.getheight() / 2;
int i;
setlinecolor(EXPLOSIONCOLOR);
for (i = 1; i <= 10; i++)
{
circle(x, y, i);
Sleep(10);
}
for (i = 1; i <= 16; i++)
{
circle(iPlayerX, iPlayerY + 11, i);
Sleep(10);
}
for (i = 1; i <= 32; i++)
{
setlinecolor((i % 2 == 0) ? 0x54A8FC : 0x5400FC);
circle(iPlayerX, iPlayerY, i);
Sleep(10);
}
for (i = 48; i >= 1; i--)
{
setlinecolor(BACKATTR);
circle(iPlayerX, iPlayerY, i);
Sleep(10);
}
fillcircle(iPlayerX, iPlayerY, 48);
return iPlayerHit;
}
11.最后還可以添加一下死亡之后的游戲動畫(比如跳舞慶祝勝利者)
// 繪制跳舞的大猩猩(勝利后執(zhí)行)
// 參數(shù):
// idPlayer:游戲者編號
void VictoryDance(int idPlayer)
{
for (int i = 0; i < 4; i++)
{
putimage(g_ptGorilla[idPlayer].x, g_ptGorilla[idPlayer].y, &g_imgGorL);
Sleep(200);
putimage(g_ptGorilla[idPlayer].x, g_ptGorilla[idPlayer].y, &g_imgGorR);
Sleep(200);
}
}
12.主函數(shù)(把所有的功能函數(shù)放這里來)
void main()
{
Init();
Intro();
PlayGame(_T("Player 1"), _T("Player 2"));
}
大家趕緊去動手試試吧!
審核編輯:湯梓紅
-
游戲
+關注
關注
2文章
787瀏覽量
27237 -
C語言
+關注
關注
183文章
7642瀏覽量
144519 -
編程
+關注
關注
90文章
3707瀏覽量
96709 -
源碼
+關注
關注
8文章
682瀏覽量
31059
原文標題:C語言項目:扔香蕉的大猩猩(自制游戲)!詳細思路+源碼分享
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學習基地】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄

C語言項目:扔香蕉的大猩猩(自制游戲)!詳細思路+源碼分享
評論