C/C++ 初级球球大作战练手

效果演示:

https://live.csdn.net/v/385490

游戏初始化

  • #include <stdbool.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<time.h>
    #include<graphics.h>
    #include <algorithm>
    #include<math.h>
    #include<mmsystem.h>
    #include <iostream>
    using namespace std;
    #pragma comment(lib,"winmm.lib")  
    //所需要的库引入
  • 需要准备的相关宏定义:
    #define WIN_WIDTH 1024 
    #define WIN_HEIGHT 640
    #define MAP_WIDTH  (WIN_WIDTH*5)
    #define MAP_HEIGHT (WIN_HEIGHT*5)
    #define FOOD_NUM 1000
    #define AI_NUM 500
  • 初始化显示终端:initgraph(1024,640)
    int main() {initgraph(WIN_WIDTH, WIN_HEIGHT);//初始化界面gameinit();while (true) {aimove();gamedraw();eatfood();playercontroller(10);}/*getchar();*/return 0;
    }
  • 根据main函数主体逻辑串联整个代码体系

游戏初始化操作

  • 游戏 初始化:在已经有的显示画面上进行布置球球的操作
  • 通过随机种子 :随机布置食物球球的位置  玩儿家的随机小球位置  自由移动的小球的位置
  • 前置定义:玩儿家,自由移动小球,食物小球
    struct ball
    {int x;int y;int r;bool flag;DWORD color;
    }player,food[FOOD_NUM],ai[AI_NUM];

进入游戏的主题循环

  • 自由小球的移动设置
    void Chase(struct ball* chase,struct ball run)
    {chase->x < run.x ? chase->x += 2 : chase->x -= 2;chase->y < run.y ? chase->y += 2 : chase->y -= 2;
    }void aimove()
    {for (int i = 0; i < AI_NUM; i++){if (ai[i].flag){Chase(&ai[i], player);}}
    }
    
  1. 通过循环遍历 ai 数组。
  2. 检查每个元素的 flag 是否为真(即激活状态)。
  3. 如果是真,调用 Chase 函数,将 ai[i](追击者的位置)和 player(目标的位置)作为参数传递给 Chase
  4. 如果 chase->x(追击者的横坐标)小于 run.x(目标的横坐标),则将 chase->x 增加 2。
  5. 否则(即 chase->x 不小于 run.x),将 chase->x 减少 2。
  6. 同理,对于纵坐标 y,如果 chase->y 小于 run.y,则 chase->y 增加 2;否则 chase->y 减少 2
  • 缺乏边界检查:可按照如下的案例变动:
    #include <limits.h> // 引入INT_MAX以确保不会除以零void Chase(struct ball* chase, struct ball run) {int dx = run.x - chase->x; // 计算目标和追击者之间的横坐标差int dy = run.y - chase->y; // 计算目标和追击者之间的纵坐标差// 确保不会除以零if (dx == 0 && dy == 0) {return;}// 限制移动速度int max_speed = 5; // 假设最大移动速度为5int speed = fmin(abs(dx), abs(dy)) * (dx < 0 ? -1 : 1); // 计算实际移动速度speed = fmin(speed, max_speed); // 限制速度不超过最大值// 更新追击者的位置if (dx < 0) {chase->x += speed;} else if (dx > 0) {chase->x -= speed;}if (dy < 0) {chase->y += speed;} else if (dy > 0) {chase->y -= speed;}// 确保移动后的位置不会超出边界int max_x = INT_MAX; // 假设横坐标的最大值int max_y = INT_MAX; // 假设纵坐标的最大值int min_x = INT_MIN; // 假设横坐标的最小值int min_y = INT_MIN; // 假设纵坐标的最大值chase->x = fmin(fmax(chase->x, min_x), max_x);chase->y = fmin(fmax(chase->y, min_y), max_y);
    }void aimove() {for (int i = 0; i < AI_NUM; i++) {if (ai[i].flag) {Chase(&ai[i], player);// 这里可以添加其他逻辑,例如检查玩家是否在AI的视野范围内}}
    }

进入游戏绘制

  • 本质:在有了结构体 相当于有了对象,根据结构体对象,使其在画面上显示
  •  
    IMAGE map(MAP_WIDTH, MAP_HEIGHT);
    POINT cameraPos;void CameraUpdate()
    {cameraPos.x = player.x - WIN_WIDTH / 2;cameraPos.y = player.y - WIN_HEIGHT / 2;if (cameraPos.x < 0) cameraPos.x = 0;if (cameraPos.y < 0) cameraPos.y = 0;if (cameraPos.x > MAP_WIDTH - WIN_WIDTH) cameraPos.x = MAP_WIDTH - WIN_WIDTH;if (cameraPos.y > MAP_HEIGHT - WIN_HEIGHT) cameraPos.y = MAP_HEIGHT - WIN_HEIGHT;}void gamedraw() {BeginBatchDraw();SetWorkingImage(&map);//设置图像setbkcolor(WHITE);//设置背景颜色cleardevice();//准备工作//绘制图像//1.食物圆for (int i = 0; i< FOOD_NUM; i++) {if (food[i].flag) {setfillcolor(food[i].color);solidcircle(food[i].x, food[i].y, food[i].r);}}//2.ai圆for (int i = 0; i < AI_NUM; i++){if (ai[i].flag){setfillcolor(ai[i].color);solidcircle(ai[i].x, ai[i].y, ai[i].r);}}//3.玩儿家圆if (player.flag){setfillcolor(player.color);solidcircle(player.x, player.y, player.r);settextcolor(BLACK);setbkmode(TRANSPARENT);// 确保文件编码支持中文,并且项目设置使用MBCSouttextxy(player.x, player.y, "百年好合");}//显示页面SetWorkingImage();CameraUpdate();putimage(0, 0, WIN_WIDTH, WIN_HEIGHT, &map, cameraPos.x, cameraPos.y);EndBatchDraw();
    }

  • BeginBatchDraw();: 开始批处理绘图,这通常意味着接下来所有的绘图操作都将被累积起来,直到 EndBatchDraw 被调用时一次性渲染到屏幕上。

    SetWorkingImage(&map);: 设置当前工作图像为 map,这通常是游戏中的地图或者背景图像

  • setbkcolor(WHITE);: 设置背景颜色为白色。

  • cleardevice();: 清除设备,通常是清除屏幕,使其变为背景颜色。

  • 关注代码设计的核心逻辑  置于陌生的调用方法 了解能用即可

现在整个画面已经布局好 但是静态的  需要令其动起来

游戏运行  吃食物 根据按键动作

  • 吃食物:遇见食物的坐标自身的小球的半径扩大即可
  • double DisTance(struct ball b1, struct ball b2)
    {return sqrt((double)(b1.x - b2.x) * (b1.x - b2.x) + (b1.y - b2.y) * (b1.y - b2.y));
    }void eatfood() {//吃食物的逻辑  :  遇见食物就圆扩大for (int i = 0; i < FOOD_NUM; i++){if (food[i].flag && DisTance(player, food[i]) < player.r){food[i].flag = false;player.r += food[i].r / 4;}}
    }

    函数 DisTance

    DisTance 函数计算两个球体之间的欧几里得距离。这个函数对于游戏中的碰撞检测非常有用,比如判断玩家控制的球体是否接触到食物。函数的逻辑如下:

  • 接受两个 ball 类型的参数 b1 和 b2,代表两个球体的位置。
  • 返回两个球体之间的距离,使用欧几里得距离公式:(𝑥2−𝑥1)2+(𝑦2−𝑦1)2(x2​−x1​)2+(y2​−y1​)2​。

        函数 eatfood  函数处理玩家球体吃食物的逻辑。当玩家球体接触到食物时,球体会变大。函数的逻辑如下:

  • 遍历 food 数组,该数组包含多个食物元素。
  • 对于每个食物元素,检查它是否激活(food[i].flag 为 true)以及玩家球体与食物之间的距离是否小于玩家球体的半径(player.r)。
  • 如果条件满足,说明玩家球体已经吃到食物,将食物的 flag 设置为 false,表示食物已被吃掉。
  • 增加玩家球体的半径,增加的大小是食物半径的四分之一(food[i].r / 4)。这表示玩家球体在吃到食物后会变大。

游戏核心玩儿家运作

  • 实质:根据按键  玩家控制小球的移动
  • void playercontroller(int speed) {//根据按键玩家控制移动if (GetAsyncKeyState(VK_UP) && player.y - player.r >= 0){player.y -= speed;}if (GetAsyncKeyState(VK_DOWN) && player.y + player.r < MAP_HEIGHT){player.y += speed;}if (GetAsyncKeyState(VK_LEFT) && player.x - player.r >= 0){player.x -= speed;}if (GetAsyncKeyState(VK_RIGHT) && player.x + player.r < MAP_WIDTH){player.x += speed;}
    }

    使用了 GetAsyncKeyState 函数来检测按键的状态

  • if (GetAsyncKeyState(VK_UP) && player.y - player.r >= 0): 这行代码检查 ‘上’ 箭头键是否被按下,并且玩家的当前位置的 y 坐标减去其半径是否大于或等于 0。如果这两个条件都为真,意味着玩家不在屏幕底部,可以向上移动。

  • player.y -= speed;: 如果 ‘上’ 箭头键被按下,并且玩家不在屏幕底部,玩家的 y 坐标会减去速度值,这样玩家就会在游戏世界中向上移动。

  • if (GetAsyncKeyState(VK_UP) && player.y - player.r >= 0): 这行代码检查 ‘上’ 箭头键是否被按下,并且玩家的当前位置的 y 坐标减去其半径是否大于或等于 0。如果这两个条件都为真,意味着玩家不在屏幕底部,可以向上移动。

  • player.y -= speed;: 如果 ‘上’ 箭头键被按下,并且玩家不在屏幕底部,玩家的 y 坐标会减去速度值,这样玩家就会在游戏世界中向上移动。。。。。。。。。。

补充:

`CameraUpdate` 函数是用于更新游戏相机位置的函数。相机在游戏中的作用是让玩家能够看到游戏世界的一部分,通常是一个窗口(视口)。这个函数确保相机的位置始终在游戏世界的可视区域内。
以下是 `CameraUpdate` 函数的原理和逻辑:
1. `cameraPos.x = player.x - WIN_WIDTH / 2;`:计算相机在 x 轴的位置。相机的 x 坐标是玩家的 x 坐标减去屏幕宽度的一半。这样,相机就会在玩家正中间的位置。
2. `cameraPos.y = player.y - WIN_HEIGHT / 2;`:计算相机在 y 轴的位置。相机的 y 坐标是玩家的 y 坐标减去屏幕高度的一半。这样,相机就会在玩家正中间的位置。
3. `if (cameraPos.x < 0) cameraPos.x = 0;`:如果相机的位置在 x 轴上小于 0,则将相机的位置设置为 0。这意味着相机不能移动到游戏世界的左侧边界之外。
4. `if (cameraPos.y < 0) cameraPos.y = 0;`:如果相机的位置在 y 轴上小于 0,则将相机的位置设置为 0。这意味着相机不能移动到游戏世界的顶部边界之外。
5. `if (cameraPos.x > MAP_WIDTH - WIN_WIDTH) cameraPos.x = MAP_WIDTH - WIN_WIDTH;`:如果相机的位置在 x 轴上大于游戏世界的宽度减去屏幕宽度,则将相机的位置设置为游戏世界的右侧边界。
6. `if (cameraPos.y > MAP_HEIGHT - WIN_HEIGHT) cameraPos.y = MAP_HEIGHT - WIN_HEIGHT;`:如果相机的位置在 y 轴上大于游戏世界的高度减去屏幕高度,则将相机的位置设置为游戏世界的底部边界。
通过这些逻辑,`CameraUpdate` 函数确保相机始终在游戏世界的可视区域内,无论玩家在游戏世界中如何移动。这对于保持玩家在屏幕上的中心位置并显示游戏世界的重要部分非常重要。

总结:至此简单实现的游戏完结,主要在于理清思路,拿此训练思维,孰能生巧。至于高级低级全看个人,随之成长日趋强键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/833882.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java多线程编码应用1——java多线程CompletableFuture使用技巧

在实际项目开发过程中&#xff0c;大部分程序的执行顺序都是按照代码编写的先后顺序&#xff0c;依次从上往下挨个执行的&#xff0c;但是对于统计或者批量操作数据时&#xff0c;是否有更好的方案呢&#xff1f;这时候就可以考虑使用多线程编程&#xff0c;异步并行执行多个任…

【MATLAB画图】如何绘制图像坐标系

首先我们需要图像坐标轴的原点在左上角&#xff1a; set(gca,ydir,reverse,xaxislocation,top); 然后我们需要坐标轴上加上箭头 quiver(0, 0, 0, 520); % 在(x1, y1)处绘制一个箭头&#xff0c;其方向和长度由(dx, dy)确定 quiver(0, 0, 700, 0); % 在(x1, y1)处绘制一个箭头…

英语新概念2-回译法-lesson13

The Greenwood Boys 绿林少年是一组流行歌手们。现在他们正在参观城市里的所有公园&#xff0c;他们明天就要到这。他们将坐火车到并且大多数小镇上的年轻人将要欢迎他们&#xff0c;明天晚上他们将要在工人俱乐部唱歌。绿林少年将在这待五天&#xff0c;在这期间&#xff0c;…

flowable一对并发网关跳转的分析

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

前端面试题大合集3----网络篇

目录 一、Http协议详解&#xff0c;http请求方式&#xff0c;http状态码 Http协议详解&#xff1a; http请求方式&#xff1a; http状态码&#xff1a; 常用的状态码&#xff1a; 其他常用状态码&#xff1a; 二、Http常见请求方式 三、Http协议与TCP协议的区别和联系 …

【STM32G474】利用Cpp编写STM32代码后,Cubemx修改配置后代码报错147个error,如何处理?

问题描述 打开Cubemx&#xff0c;添加TIM7用于定时器精准延时&#xff0c;生成代码后&#xff0c;Keil提示有147个error。 之前是Cubemx是没有问题的&#xff0c;是利用Cpp编写stm32&#xff08;将Keil改为Version6&#xff09;后才导致Cubemx配置失败&#xff1a; debug成功…

Mybatis进阶2

Mybatis进阶1-CSDN博客 Mybatis入门-CSDN博客 Mybatis入门2-CSDN博客 我们接下来要学习Mybatis的高级查询 我们先在数据库中准备我们需要的数据表 teacher表 课程表&#xff1a;与教师表是一对多的关系&#xff0c;所以有一个外键字段 学生表 由于学生表和课程表是多对多的…

《Python编程从入门到实践》day22

# 昨日知识点回顾 方法重构、驾驶飞船左右移动、全屏显示 飞船不移动解决&#xff0c;问题出在移动变量x更新 # Ship.pysnipdef update(self):"""根据移动标志调整飞船的位置"""# 更新飞船而不是rect对象的x值# 如果飞船右移的标志和飞船外接…

射频无源器件之电桥

一. 电桥的定义及作用 电桥主要用于实现微波大功率功放系统的功率合成分配,信号采集等功能,被广泛应用于中国及全球4G/5G基站、5G网络覆盖、北斗导航天线、车载高精度导航(无人驾驶)天线等。可将信号分成有相位差的两路,90度电桥相位差90,180度电桥相位差180。 常说的3d…

Redis学习2——SpringBoot整合Redis,Redis工具类

依赖和配置 pom.xml SpringBoot整合Redis&#xff0c;需要引入spring-boot-starter-data-redis依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>当…

stm32_RTC_2_HAL——stm32CudeMX

介绍 RTC&#xff08;实时时钟&#xff09;不仅仅提供计数功能&#xff0c;它是一个完整的时钟和日历模块&#xff0c;用于提供日期和时间信息。RTC 能够提供年、月、日、星期、时、分、秒等时间信息&#xff0c;并且通常具有闹钟功能&#xff0c;可以用于定时唤醒或触发事件。…

C++学习第十二天(继承)

1、继承的概念以及定义 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行拓展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结构&#x…

STM32F103学习笔记 | 报错界面及解决方案 | 1.keil5中文注释的横竖(正与斜)问题

文章目录 一、报错界面二、解决方案参考文献 一、报错界面 二、解决方案 打开设置 在打开的设置选项卡中&#xff0c;图中Font显示的是这个软件当前设置的字体&#xff0c;可以看到字体是仿宋&#xff0c;这就是问题出现的原因&#xff0c;将之改成没有的字体就行了。 可以看…

用FPGA+DAC输出“心”形波

1.前言 之前在做信号处理的时候整了一下活&#xff0c;用FPGADAC&#xff08;数模转换器&#xff09;&#xff0c;输出了一个爱心形状的波形&#xff0c;今天整理资料的时候偶然发现了他&#xff0c;现在把他分享出来。当时将DAC的输出接在示波器上显示如下图所示&#xff1a; …

Docker使用进阶篇

文章目录 1 前言2 使用Docker安装常用镜像示例2.1 Docker安装RabbitMQ2.2 Docker安装Nacos2.3 Docker安装xxl-job&#xff08;推荐该方式构建&#xff09;2.4 Docker安装redis2.5 Docker安装mysql 3 Docker自定义镜像3.1 Dockerfile的基本结构3.2 Dockerfile指令3.3 自定义JDK镜…

Flutter笔记:手动配置VSCode中Dart代码自动格式化

Flutter笔记 手动配置VSCode中Dart代码自动格式化 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csd…

数据结构学习——线性表、顺序表

1.线性表 线性表 &#xff08; linear list &#xff09; 是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一…

在WPS表格(Excel)中,每10行增加一个特定的值

注&#xff1a;如下为WPS表格操作演示 例如1&#xff0d;15的数值是1&#xff0c;16-30就变为2&#xff0c;31-45就变为3&#xff0c;类推&#xff01; 1、在B1单元格输入一个起始值&#xff0c;B2单元格输入公式IF(MOD(ROW(),15)0,B11,B1) 然后鼠标放到B2单元格右下角小点处&…

利用生成式AI重新构想ITSM的未来

对注入 AI 的生成式 ITSM 的需求&#xff0c;在 2023 年 Gartner AI 炒作周期中&#xff0c;生成式 AI 达到预期值达到顶峰后&#xff0c;三分之二的企业已经将生成式 AI 集成到其流程中。 你问为什么这种追求&#xff1f;在预定义算法的驱动下&#xff0c;IT 服务交付和管理中…

第七届机电、机器人与自动化国际会议(ICMRA 2024)即将召开!

第七届机电、机器人与自动化国际会议&#xff08;ICMRA 2024&#xff09;将于2024年9月20日-22日在中国武汉举行。ICMRA 2024为各国专家学者提供一个学术交流的平台&#xff0c;讨论机电、机器人和自动化领域的最新研究成果和未来的研究方向&#xff0c;旨在能够建立起国家间&a…