刚上大一,我相信很多计算机专业或者计算机相关专业的学生好不容易怀着一颗澎湃的心成功学完一门语言,却发现自己啥也不会。开始渐渐怀疑自己,开始迷茫,不知所措。虽然我不是计算机专业的学生,但作为一名23届二本信管专业的我,我至少没有放弃!我希望每一个人都不要因为专业为借口而选择逃避、选择自我否认,难道项目一定要非得是计算机专业的学生才能做吗?千万不要有这种想法,所以我想告诉大家迷茫是正常的,之所以迷茫说明你正在找属于的道路,至少你不是选择躺平、寝室一天天待着打游戏,这已经说明你已经超过了70%共同阶段的人了,所以为什么坚定起来呢,给自己一点时间,去找方向、去找目标、去找未来。
我的第一门语言是C语言,我们大一上学的是企业经营管理,大一下才学C语言,我也是大一,我也迷茫过,但是我会在迷茫的阶段中花最短的时间去找到自己的方向、目标、未来,老是很想快速定位自己,一天天过去,以免浪费时间,我老是想学快一点,然后争取实习,想学完C语言就直接学Python或者JAVA等等或者想早早的定位自己,但我始终下不了决定,于是我与教我的第一门语言的老师交流,我觉得她所跟我说的,给我带来挺多的价值的。所以我想在大家学习项目之前分享大家,虽然不是适合每一个人,但我觉得值得分享。
我的第一门语言是曾老师所教,她的这句话点醒了我:“不要先去早早的定位自己,未来的事情我们都不知道会发生什么,只有不断做好当下的事才是最重要的,把握当下的事,在你学习的每一处也不是一味追求学快,而是追求学精,将不同阶段所学的知识去深造,拓展,然后将现阶段所学的知识去不断实践,最终所反馈给你的就是你的收获和成果!”希望大家细品,希望能帮助正在迷茫的你!
所以大家也千万不要太纠结,把当下事情做好就是最重要的!!!
现在开始给大家分享我大一学了一学期C语言,自己如何做小项目:仿五子棋游戏
首先,我学C语言的时候,在开学后的3个月自己几乎快速学会基本语法,所以我就开始帅刷题,刷了差不多40、50道题吧
发现有点无聊,所以就开始学习做小项目,就做了一个仿《五子棋》的小项目。
由于我是用SDL库来做的,肯定有人好奇为什么我用SDL呢??而不是其他库呢!因为SDL库属于一款纯C语言的语言编写的,因此,我才使用它来做这次的小项目,所以,大家可以必须先给自己的编译器配置SDL环境!
SDL用到的文件包有:SDL.2和SDL_image以及SDL_ttf,SDL_ttf可以先不用。
环境配置好之后,接下来我们就来看看如何开发小项目吧!
第一步:写好游戏脚本!
首先我们要明确,要做个简单的五子棋我们应该准备什么呢???没错,就写好游戏脚本!!
五子棋游戏脚本:1.建立窗口
2.建立棋盘窗口
3.绘制棋盘
4.纹理棋子(以及自己想要的纹理)
5.记录每一颗棋子的位置
6.判断输赢
7.玩家输赢信息打印
8.创建一个游戏菜单,进行更好的体验模式(可惜我能力有限,虽然我做好了game_menu,不会做两个代码的接口,所以在此我就不展示这个游戏菜单的链接了)
第二步:毫无疑问就是敲代码啦!
下面就是整个五子棋代码环节:
代码头文件:
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include<SDL_ttf.h>
SDL初始化:
//初始化SDL检查
void init_sdl()
{
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
SDL_Log("%s\n", SDL_GetError());
exit(0);
}
}
窗口以及渲染文件路径和数组初始化:
SDL_Window* w;
SDL_Renderer* r;
SDL_Texture* t1,*t2,*t3,*t4,*t5,*t6,*t7;//纹理的对象
SDL_Point mousepos;
bool isRunning = true;
int width = 1000, height = 800;//窗口大小
//文件所需路径
const char* path_w = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\2.png";
const char* path_b = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\1.png";
const char* path_mid = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\3.png";
const char* path_angle = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\6.png";
const char* path_hum1 = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\9.png";
const char* path_hum2 = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\10.png";
const char* path_deb = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\12.png";
const char* path_text ="C:\\Program Files (x86)\\DingDing\\main\\current\\data\\flutter_assets\\fonts\\iconfont.ttf" ;
//二维数组用来做棋盘索引(最好不要用一维,不然会死的很难看的,一维数组的话交复杂)
int board[15][15] = { {0} };
int player = -1;//这里的目的是判断谁先手《黑:-1》<<------>>《白:2》
建立窗口:
//建立窗口
void create_window(int width,int height)
{
w=SDL_CreateWindow(u8"五子棋", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
r = SDL_CreateRenderer(w, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawColor(r, 255, 255, 178, 255);
SDL_RenderClear(r);
SDL_RenderPresent(r);
}
绘制棋盘:(大家可以自己选择是否要为了棋盘美观设置一下点、角色等等,若觉得难度大,即写两个for循环画棋盘表格就行了,大家一定要注意了,棋盘最好设置大小为15*15,不然后面出现bug太难修复了,我就是一个很好的列子,棋盘默认也是15*15大小,所以各位小伙伴们绘画一个15*15的表格最佳,防止出现各种bug。我之前做了15*20的,遇到了bug,检查了好久,一直找不到问题,修复不了,所以选择15*15)
//创建棋盘
void create_chessline(int width,int height)
{
SDL_SetRenderDrawColor(r, 0, 0, 0, 255);
for (int x = 50;x <= width - 200;x += 50) {
SDL_RenderDrawLine(r, x, 10, x, height - 40);
}
for (int y = 10;y <= height - 40;y += 50) {
SDL_RenderDrawLine(r, 50, y, width - 200,y);
}
//给棋盘中间绘个红点(由于我的棋盘为15*15的,所以选择8行8列做为中心)
SDL_Surface* chess_mid = IMG_Load(path_mid);
t3 = SDL_CreateTextureFromSurface(r, chess_mid);
SDL_Rect dst1 = { 450 - 7, 353, 15, 15 };
SDL_RenderCopy(r, t3, NULL, &dst1);
//四角为四个黑点(大家可以随意定制,对称即可)
SDL_Surface* chess_angle = IMG_Load(path_angle);
t4 = SDL_CreateTextureFromSurface(r, chess_angle);
SDL_Rect dst2 = { 696,106,10,10 };
SDL_Rect dst3 = { 696,656,10,10 };
SDL_Rect dst4 = { 146,106,10,10 };
SDL_Rect dst5 = { 146,656,10,10 };
SDL_Rect dst6 = { 146,656,10,10 };
SDL_Rect dst7 = { 146,656,10,10 };
SDL_RenderCopy(r, t4, NULL, &dst2);
SDL_RenderCopy(r, t4, NULL, &dst3);
SDL_RenderCopy(r, t4, NULL, &dst4);
SDL_RenderCopy(r, t4, NULL, &dst5);
//角色扮演人物
SDL_Surface* chess_humaner1 = IMG_Load(path_hum1);
SDL_Surface* chess_humaner2 = IMG_Load(path_hum2);
SDL_Surface* chess_debat = IMG_Load(path_deb);
t5 = SDL_CreateTextureFromSurface(r, chess_humaner1);
t6 = SDL_CreateTextureFromSurface(r, chess_humaner2);
t7 = SDL_CreateTextureFromSurface(r, chess_debat);
SDL_Rect dst8 = {800,10,210,210};
SDL_Rect dst9 = { 790,530,220,220 };
SDL_Rect dst10 = { 830,300,160,160 };
SDL_RenderCopy(r, t5, NULL, &dst8);
SDL_RenderCopy(r, t6, NULL, &dst9);
SDL_RenderCopy(r, t7, NULL, &dst10);
SDL_RenderPresent(r);
}
纹理棋子:(大家自己一定要找对自己文件图片信息的路径)
//加载图片、纹理棋子
void texture_chess()
{
SDL_Surface* white = IMG_Load(path_w);
SDL_Surface* black = IMG_Load(path_b);
t1 = SDL_CreateTextureFromSurface(r, white);
t2 = SDL_CreateTextureFromSurface(r, black);
SDL_FreeSurface(white);
SDL_FreeSurface(black);
}
记录每一颗棋子的位置:(这里一定要注意,一定要将自己的物理坐标(也就是屏幕坐标)转化为逻辑坐标(也就是从0开始索引的坐标)以便为游戏判断做铺垫,并且设置好鼠标的范围,防止超出了棋盘的界限)
//记录棋置
void previous_chess(int x, int y)
{
// 计算格子逻辑坐标(好用来索引)
int grid_x = (x - 50) / 50;
int grid_y = (y - 10) / 50;
// 确保棋子绘制位置在棋盘范围内
if (grid_x >= 0 && grid_x < 15 && grid_y >= 0 && grid_y < 15) {
// 棋子可以在这里下
if (board[grid_x][grid_y] == 0) {
SDL_Rect dst;
dst.h = 30;
dst.w = 30;
dst.x = grid_x * 50 + 50 + 10;
dst.y = grid_y * 50 + 10 + 10;
SDL_Texture* nowT = (player == -1) ? t1 : t2;
SDL_RenderCopy(r, nowT, NULL, &dst);
player = 1 - player;
board[grid_x][grid_y] = player;
printf("逻辑坐标:(%d, %d)\n", grid_x, grid_y);
}
}
SDL_RenderPresent(r);
}
设置获胜一个函数:(游戏获胜时方可知道哪一方去得胜利)
// 显示获胜消息框
void show_winner_message(int player) {
char* message;
if (player == 2) {
message = SDL_strdup(u8"噢耶!恭喜白方获胜!");
}
else {
message = SDL_strdup(u8"噢耶!恭喜黑方获胜!");
}
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, u8"游戏结束", message, NULL);
SDL_free(message);
}
就比如比如这样子 :
判断输赢(最核心):(这里就是最核心的点了,注意判断输赢在于:水平、垂直、左对角线、和右对角线的判断,我曾经也在这里死过,因为我用一维数组,诶,所以为什么我开始一定强调大家一定要用二维数组做索引,因为二维数组做索引,刚刚好对应棋盘的行和列,所以二维数组的选择是最佳的选择!)
//判断输赢(行:15 列:15)
bool check_win(int board[15][15], int player)
{
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
// 水平方向检查
if (j + 1 < 15 &&
board[i][j + 0] == player &&
board[i][j + 1] == player &&
board[i][j + 2] == player &&
board[i][j + 3] == player &&
board[i][j + 4] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
// 垂直方向检查
if (i + 1 < 15 &&
board[i][j] == player &&
board[i + 1][j] == player &&
board[i + 2][j] == player &&
board[i + 3][j] == player &&
board[i + 4][j] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
// 右斜方向检查(左上到右下)
if (i + 4 < 15 && j + 4 < 15 &&
board[i][j] == player &&
board[i + 1][j + 1] == player &&
board[i + 2][j + 2] == player &&
board[i + 3][j + 3] == player &&
board[i + 4][j + 4] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
// 左斜方向检查(右上到左下)
if (i + 4 < 15 && j - 4 > 0 &&
board[i][j] == player &&
board[i + 1][j - 1] == player &&
board[i + 2][j - 2] == player &&
board[i + 3][j - 3] == player &&
board[i + 4][j - 4] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
}
}
return false;
}
事件循环:
//事件循环
void event_loop()
{
SDL_Event event;
while (isRunning) {
if (SDL_PollEvent(&event) != 0) {
switch (event.type) {
case SDL_QUIT:
isRunning = false;
break;
case SDL_MOUSEBUTTONDOWN:
SDL_GetMouseState(&mousepos.x, &mousepos.y);
if (mousepos.x > 50 && mousepos.x < width - 200 && mousepos.y >10 && mousepos.y < height - 40) {
previous_chess(mousepos.x, mousepos.y);
check_win(board, player);
SDL_Log("%-4d%4d",mousepos.x ,mousepos.y);
}
break;
}
}
}
}
清理释放:
//清理释放
void clean_up()
{
SDL_DestroyWindow(w);
SDL_DestroyRenderer(r);
SDL_DestroyTexture(t1);
SDL_DestroyTexture(t2);
SDL_DestroyTexture(t3);
SDL_DestroyTexture(t4);
SDL_DestroyTexture(t5);
SDL_DestroyTexture(t6);
SDL_DestroyTexture(t7);
IMG_Quit();
SDL_Quit();
}
主调函数:
//主函数
int main(int argc, char* argv[]) {
init_sdl();
create_window(width , height );
create_chessline(width, height);
texture_chess();
event_loop();
clean_up();
return 0;
}
没错,这就是一个通过SDL写出来的五子棋游戏!总共268行代码,只是我为了美观,所以纹理一其他的东西,如果纯属写游戏的话,应该用不了这么多!!!
以下属于完整代码:
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include<SDL_ttf.h>
SDL_Window* w;
SDL_Renderer* r;
SDL_Texture* t1,*t2,*t3,*t4,*t5,*t6,*t7;//纹理的对象
SDL_Point mousepos;
bool isRunning = true;
int width = 1000, height = 800;//窗口大小
//文件所需路径
const char* path_w = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\2.png";
const char* path_b = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\1.png";
const char* path_mid = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\3.png";
const char* path_angle = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\6.png";
const char* path_hum1 = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\9.png";
const char* path_hum2 = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\10.png";
const char* path_deb = "C:\\Users\\Yzynl\\Desktop\\SDL_image\\12.png";
const char* path_text ="C:\\Program Files (x86)\\DingDing\\main\\current\\data\\flutter_assets\\fonts\\iconfont.ttf" ;
//二维数组用来做棋盘索引(最好不要用一维,不然会死的很难看的,一维数组的话交复杂)
int board[15][15] = { {0} };
int player = -1;//这里的目的是判断谁先手《黑:-1》<<------>>《白:2》
//初始化SDL检查
void init_sdl()
{
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
SDL_Log("%s\n", SDL_GetError());
exit(0);
}
}
//建立窗口
void create_window(int width,int height)
{
w=SDL_CreateWindow(u8"五子棋", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
r = SDL_CreateRenderer(w, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawColor(r, 255, 255, 178, 255);
SDL_RenderClear(r);
SDL_RenderPresent(r);
}
//创建棋盘
void create_chessline(int width,int height)
{
SDL_SetRenderDrawColor(r, 0, 0, 0, 255);
for (int x = 50;x <= width - 200;x += 50) {
SDL_RenderDrawLine(r, x, 10, x, height - 40);
}
for (int y = 10;y <= height - 40;y += 50) {
SDL_RenderDrawLine(r, 50, y, width - 200,y);
}
//给棋盘中间绘个红点(由于我的棋盘为15*15的,所以选择8行8列做为中心)
SDL_Surface* chess_mid = IMG_Load(path_mid);
t3 = SDL_CreateTextureFromSurface(r, chess_mid);
SDL_Rect dst1 = { 450 - 7, 353, 15, 15 };
SDL_RenderCopy(r, t3, NULL, &dst1);
//四角为四个黑点(大家可以随意定制,对称即可)
SDL_Surface* chess_angle = IMG_Load(path_angle);
t4 = SDL_CreateTextureFromSurface(r, chess_angle);
SDL_Rect dst2 = { 696,106,10,10 };
SDL_Rect dst3 = { 696,656,10,10 };
SDL_Rect dst4 = { 146,106,10,10 };
SDL_Rect dst5 = { 146,656,10,10 };
SDL_Rect dst6 = { 146,656,10,10 };
SDL_Rect dst7 = { 146,656,10,10 };
SDL_RenderCopy(r, t4, NULL, &dst2);
SDL_RenderCopy(r, t4, NULL, &dst3);
SDL_RenderCopy(r, t4, NULL, &dst4);
SDL_RenderCopy(r, t4, NULL, &dst5);
//角色扮演人物
SDL_Surface* chess_humaner1 = IMG_Load(path_hum1);
SDL_Surface* chess_humaner2 = IMG_Load(path_hum2);
SDL_Surface* chess_debat = IMG_Load(path_deb);
t5 = SDL_CreateTextureFromSurface(r, chess_humaner1);
t6 = SDL_CreateTextureFromSurface(r, chess_humaner2);
t7 = SDL_CreateTextureFromSurface(r, chess_debat);
SDL_Rect dst8 = {800,10,210,210};
SDL_Rect dst9 = { 790,530,220,220 };
SDL_Rect dst10 = { 830,300,160,160 };
SDL_RenderCopy(r, t5, NULL, &dst8);
SDL_RenderCopy(r, t6, NULL, &dst9);
SDL_RenderCopy(r, t7, NULL, &dst10);
SDL_RenderPresent(r);
}
//加载图片、纹理棋子
void texture_chess()
{
SDL_Surface* white = IMG_Load(path_w);
SDL_Surface* black = IMG_Load(path_b);
t1 = SDL_CreateTextureFromSurface(r, white);
t2 = SDL_CreateTextureFromSurface(r, black);
SDL_FreeSurface(white);
SDL_FreeSurface(black);
}
//记录棋置
void previous_chess(int x, int y)
{
// 计算格子逻辑坐标(好用来索引)
int grid_x = (x - 50) / 50;
int grid_y = (y - 10) / 50;
// 确保棋子绘制位置在棋盘范围内
if (grid_x >= 0 && grid_x < 15 && grid_y >= 0 && grid_y < 15) {
// 棋子可以在这里下
if (board[grid_x][grid_y] == 0) {
SDL_Rect dst;
dst.h = 30;
dst.w = 30;
dst.x = grid_x * 50 + 50 + 10;
dst.y = grid_y * 50 + 10 + 10;
SDL_Texture* nowT = (player == -1) ? t1 : t2;
SDL_RenderCopy(r, nowT, NULL, &dst);
player = 1 - player;
board[grid_x][grid_y] = player;
printf("逻辑坐标:(%d, %d)\n", grid_x, grid_y);
}
}
SDL_RenderPresent(r);
}
// 显示获胜消息框
void show_winner_message(int player) {
char* message;
if (player == 2) {
message = SDL_strdup(u8"噢耶!恭喜白方获胜!");
}
else {
message = SDL_strdup(u8"噢耶!恭喜黑方获胜!");
}
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, u8"游戏结束", message, NULL);
SDL_free(message);
}
//判断输赢(行:15 列:15)
bool check_win(int board[15][15], int player)
{
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
// 水平方向检查
if (j + 1 < 15 &&
board[i][j + 0] == player &&
board[i][j + 1] == player &&
board[i][j + 2] == player &&
board[i][j + 3] == player &&
board[i][j + 4] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
// 垂直方向检查
if (i + 1 < 15 &&
board[i][j] == player &&
board[i + 1][j] == player &&
board[i + 2][j] == player &&
board[i + 3][j] == player &&
board[i + 4][j] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
// 右斜方向检查(左上到右下)
if (i + 4 < 15 && j + 4 < 15 &&
board[i][j] == player &&
board[i + 1][j + 1] == player &&
board[i + 2][j + 2] == player &&
board[i + 3][j + 3] == player &&
board[i + 4][j + 4] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
// 左斜方向检查(右上到左下)
if (i + 4 < 15 && j - 4 > 0 &&
board[i][j] == player &&
board[i + 1][j - 1] == player &&
board[i + 2][j - 2] == player &&
board[i + 3][j - 3] == player &&
board[i + 4][j - 4] == player) {
if (player == 2)
printf("恭喜白方获胜!\n");
else
printf("恭喜黑方获胜!\n");
show_winner_message(player);
isRunning = false;
return true;
}
}
}
return false;
}
//事件循环
void event_loop()
{
SDL_Event event;
while (isRunning) {
if (SDL_PollEvent(&event) != 0) {
switch (event.type) {
case SDL_QUIT:
isRunning = false;
break;
case SDL_MOUSEBUTTONDOWN:
SDL_GetMouseState(&mousepos.x, &mousepos.y);
if (mousepos.x > 50 && mousepos.x < width - 200 && mousepos.y >10 && mousepos.y < height - 40) {
previous_chess(mousepos.x, mousepos.y);
check_win(board, player);
SDL_Log("%-4d%4d",mousepos.x ,mousepos.y);
}
break;
}
}
}
}
//清理释放
void clean_up()
{
SDL_DestroyWindow(w);
SDL_DestroyRenderer(r);
SDL_DestroyTexture(t1);
SDL_DestroyTexture(t2);
SDL_DestroyTexture(t3);
SDL_DestroyTexture(t4);
SDL_DestroyTexture(t5);
SDL_DestroyTexture(t6);
SDL_DestroyTexture(t7);
IMG_Quit();
SDL_Quit();
}
//主函数
int main(int argc, char* argv[]) {
init_sdl();
create_window(width , height );
create_chessline(width, height);
texture_chess();
event_loop();
clean_up();
return 0;
}
最后给大家看看效果:
这里我没有用人机对质,因为我只会使用rand函数,因为rand函数比较简单,下棋是随机的没有策略性,我也没有学会算法,所以我没有使用人机对质,想要人机对质更强的话,需要涉及算法的问题,因为目前大一下,还没有学,所以........(哈哈你们懂得,就是我也不会,哈哈哈嗝)
最后,万事开头难!但希望大家披荆斩棘,迎难而上,不要急于求成,不用自暴自弃,更不要自我怀疑。学好编程不是一时的,而是不断的积累过程!累了就放松放松,看看夕阳,看看大海,虽然走的很慢,但是我们没有原地踏步就是最好的礼物!23届的学子们!让我们一起努力吧,加油,奥利给!!!
(写的不是很好,有什么不对的地方,希望大家可以做出指导!希望以后有什么不懂的可以多多交流!)