【C#项目实战】控制台游戏 勇士斗恶龙(2)——游戏场景的设置以及玩家战斗逻辑

在这里插入图片描述

君兮_的个人主页

即使走的再远,也勿忘启程时的初心

C/C++ 游戏开发

Hello,米娜桑们,这里是君兮_,最近开始正式的步入学习游戏开发的正轨,想要通过写博客的方式来分享自己学到的知识和经验,这就是开设本专栏的目的。希望这些独立的C#小项目能对做游戏的你有所帮助,毕竟学会游戏开发的最好的上手方式就是自己做一款游戏!!

勇士斗恶龙

  • 前言
  • 一.游戏场景的初始化
    • 1.不变场景的初始化
    • 2.玩家以及BOSS属性相关设置
  • 二.玩家移动以及战斗逻辑
    • 1.玩家的移动
      • 玩家移动的边界问题
    • 2.战斗相关代码逻辑
      • 怎么进入战斗
      • 战斗逻辑
  • 三. 游戏源码
  • 总结

前言

  • 今天我们接着来讲勇士斗恶龙的第二部分,上回咱们讲过了有关开始界面的实现以及控制台的初始化,这次我们来讲讲这个小游戏的核心内容游戏场景的初始化以及玩家的移动以及和BOSS的战斗相关代码的实现
  • 还是先把咱们游戏的整体流程图放在这里
    在这里插入图片描述

一.游戏场景的初始化

  • 上回我们说实现界面之间的转换,只需要一个while+switch就能实现 此时我们把nowSceneID(游戏场景编号)改为2, 开始对我们的游戏场景进行编辑。
  • 首先呢,我们得先把最基础的那些不需要每次都发生变化的东西先设置好

1.不变场景的初始化

  • 在这个游戏场景中,不需要我们每次都发生变化的无疑是四周的红墙了,我们优先先把这些红墙给设置好
    在这里插入图片描述
 Console.Clear();#region 4 不变的红墙//设置颜色为红色Console.ForegroundColor = ConsoleColor.Red;//画墙//上方墙for (int i = 0; i < w; i += 2){//上方墙Console.SetCursorPosition(i, 0);Console.Write("■");//下方墙Console.SetCursorPosition(i, h - 1);Console.Write("■");//中间墙Console.SetCursorPosition(i, h - 6);Console.Write("■");}//左边的墙for (int i = 0; i < h; i++){//左边的墙Console.SetCursorPosition(0, i);Console.Write("■");//右边的墙Console.SetCursorPosition(w - 2, i);Console.Write("■");}
#endregion
  • 非常简单啊,就是通过循环把设置好位置颜色的墙打印在控制台界面上,这里就不细讲了。
  • 唯一需要注意的是 对于控制台来说,一单位纵轴等于二单位的横轴,所以每次横轴要想与纵轴对应,是一次跳过两个格子的,同时我们要考虑的边界问题,注意这里红墙如果不专门设置位置左上角为(0,0) 因此我们的横轴设置的最右边的边界为w-2,最下面的边界为h-1。(如果你不理解这段话的话,自己敲代码的时候边界改成别的参数试一下,你就能清晰透彻这里这样做的原因了)

2.玩家以及BOSS属性相关设置

  • 在开始相关游戏逻辑讲解前,我们先来对游戏中的玩家和BOSS进行一下初始化,设置一下位置,以及角色对应的表示的图标
  • 同时呢,我们的玩家和BOSS是需要进行战斗的,我们在初始化的时候也需要设置一下血量和两者的攻击力方便之后战斗逻辑的编写()
#region 5 boss属性相关
//boss坐标
int bossX = 24;
int bossY = 15;
//boss攻击力的最大最小值
int bossAtkMin = 7;
int bossAtkMax = 13;
//boss血量
int bossHp = 100;
string bossIcon = "■";
//申明一个 颜色变量
ConsoleColor bossColor = ConsoleColor.Green;
#endregion#region 6 玩家属性相关
int playerX = 4;
int playerY = 5;
//玩家攻击力的最大最小值
int playerAtkMin = 8;
int playerAtkMax = 12;
//玩家血量
int playerHp = 100;
string playerIcon = "●";
ConsoleColor playerColor = ConsoleColor.Yellow;
//玩家输入的内容 外面申明 节约性能
//检测玩家的输入 方便之后的移动和攻击逻辑
char playerInput;
#endregion#region 7 玩家战斗相关
//战斗状态
bool isFight = false;//在玩家与boss战斗或者未进行战斗时应该有不同的逻辑,通过一个bool变量来判断应该走哪种逻辑
#endregion

二.玩家移动以及战斗逻辑

1.玩家的移动

  • 和正常的游戏一样,我们希望我们的玩家能够按住“WASD”来进行上下左右的移动,而移动的基本原理是什么呢?就是改变在游戏画面上显示的玩家的坐标,也就是我们的控制台上,同时呢,我们得把控制台上上一个玩家的图标给擦除掉
  • 这里又有一个问题了,我们的玩家是不能随意的进行移动的,我们必须给他的移动加上一定的范围限制,否则难不成把你的玩家移动到我们的画面外吗?因此这里又有一个毕竟复杂的问题,也就是边界讨论

玩家移动的边界问题

  • 刚才我们提到了玩家移动的边界问题,那什么是边界问题呢?我们结合我们这个游戏的的具体画面来分析一下
    在这里插入图片描述
  • 首先,在我们的游戏画面中目前是有三种物体的,第一个就是不变的墙,第二个是玩家,第三个就是我们的BOSS,我们所能控制的就是游戏界面中玩家的移动。
  • 我们玩家的移动必须在规定的范围内,也就是红墙里,这是我们设定的游戏区域,同时,我们的玩家不能与红墙的坐标重合,我们在前面说了,红墙是不变的物体,并不会随着玩家的控制而发生变化,因此我们没有必要在执行游戏的每次循环中都重复打印它,只需要在切换到游戏场景时打印它就足够了,因此如果玩家与红墙重合的话,该坐标的红墙就会消失并且在游戏没结束时就不会再打印了,同时BOSS也是,我们同样也不能让玩家与BOSS的坐标重合,尽管BOSS会在每一次循环中都打印,但是我们让玩家和BOSS能够重合,就不好处理玩家和BOSS战斗的逻辑了。
  • 通过以上的分析,我们可以得到以下玩家移动的代码
    #region 6 玩家移动相关//擦除之前位置的玩家图标Console.SetCursorPosition(playerX, playerY);Console.Write("  ");//改位置switch (playerInput){case 'W':case 'w':--playerY;//往上移动if (playerY < 1)//边界检查 如果玩家到红墙上边缘就把玩家坐标拉回去{playerY = 1;}//位置如果和boss重合了 并且boss没有死else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去++playerY;}break;case 'A':case 'a':playerX -= 2;//向左移动if (playerX < 2)边界检查 如果玩家到红墙左边缘就把玩家坐标拉回去{playerX = 2;}else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去playerX += 2;}break;case 'S':case 's':++playerY;//向下移动if (playerY > h - 7) 边界检查 如果玩家到红墙下边缘就把玩家坐标拉回去{playerY = h - 7;}else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去--playerY;}break;case 'D':case 'd':playerX += 2;  //向右移动if (playerX > w - 4)//边界检查 如果玩家到红墙右边缘就把玩家坐标拉回去{playerX = w - 4;}else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去playerX -= 2;}break;}#endregion

在这里插入图片描述

  • 如图,现在我们已经能在地图上自由的移动啦,接下来我们就来进行战斗相关的设置

2.战斗相关代码逻辑

怎么进入战斗

  • 那么怎样和BOSS进行战斗呢?首先,我们得先把我们的玩家移动到BOSS的身边,然后按下某个键进入战斗,同时把玩家的状态切换到战斗状态,此时,我们也得在下面的方框下打印一些信息来提醒玩家进入战斗可以攻击BOSS了
  case 'J':case 'j'://开始战斗if ((playerX == bossX && playerY == bossY - 1 ||playerX == bossX && playerY == bossY + 1 ||playerX == bossX - 2 && playerY == bossY ||playerX == bossX + 2 && playerY == bossY) && bossHp > 0)//玩家处于BOSS周围{isFight = true;//玩家状态切换至战斗状态//可以开始战斗,提醒一下玩家应该做什么,以及玩家和BOSS此时的血量Console.SetCursorPosition(2, h - 5);Console.ForegroundColor = ConsoleColor.White;Console.Write("开始和Boss战斗了,按J键继续");Console.SetCursorPosition(2, h - 4);Console.Write("玩家当前血量为{0}", playerHp);Console.SetCursorPosition(2, h - 3);Console.Write("boss当前血量为{0}", bossHp);}//进入战斗状态就要让玩家不能再移动//同时下方能够显示信息,提示双方造成的伤害和此时剩下的血量break;

战斗逻辑

  • 我们上面定义了一个bool类型的IsFight来判断此时的玩家是否进入战斗,当进入战斗后,我们就得进入战斗逻辑而不再是移动逻辑了,那战斗时应该是怎样的逻辑呢?
  • 首先,我们在每次进入战斗状态时都得判断一下此时双方的血量,无论双方谁的血量降为0都要进入下一环节或者游戏失败或者进入营救公主的环节
  • 然后,我们来通过双方的攻击力来轮流对对方进行攻击,我们在设置玩家和BOSS时都给予了攻击力的最大值和最小值,这样我们每次攻击时,双方都应该造成最大攻击和最小攻击之间的随机伤害,对方扣除相应的血量,这样基本的思路就捋清楚了,接一下则进入一个游戏分支
  • 当BOSS的血先减少为0时,就应该在地图上销毁BOSS并且标记出公主的位置,同时恢复玩家的行动让玩家能够去到公主身边营救公主
  • 当玩家的血量先减少为0时,说明此时玩家未能战胜BOSS,此时就应该更改场景ID进入到游戏结束界面
 //游戏场景的死循环 专门用来 检测 玩家输入相关循环while (true){//boss活着时才绘制if (bossHp > 0){//绘制boss图标Console.SetCursorPosition(bossX, bossY);Console.ForegroundColor = bossColor;Console.Write(bossIcon);}//画出玩家Console.SetCursorPosition(playerX, playerY);Console.ForegroundColor = playerColor;Console.Write(playerIcon);//得到玩家输入playerInput = Console.ReadKey(true).KeyChar;//战斗状态处理什么逻辑if (isFight){#region 7 玩家战斗相关//如果是战斗状态 你做什么//只会处理J键 if (playerInput == 'J' || playerInput == 'j'){//在这判断 玩家或者怪物 是否死亡 如果死亡了 继续之后的流程if (playerHp <= 0){//游戏结束//输掉了 应该直接显示 我们的 游戏结束界面nowSceneID = 3;break;}else if (bossHp <= 0){//去营救公主//boss擦除Console.SetCursorPosition(bossX, bossY);Console.Write("  ");isFight = false;//重新让玩家能够按wasd键移动}else{//去处理按J键打架// 玩家打怪物Random r = new Random();//得到随机攻击了int atk = r.Next(playerAtkMin, playerAtkMax);//血量减对应的攻击力bossHp -= atk;//打印信息Console.ForegroundColor = ConsoleColor.Green;//先擦除这一行 上次显示的内容Console.SetCursorPosition(2, h - 4);Console.Write("                                           ");//再来写新的信息Console.SetCursorPosition(2, h - 4);Console.Write("你对恶龙造成了{0}点伤害,boss剩余血量为{1}", atk, bossHp);// 怪物打玩家if (bossHp > 0){//得到随机攻击了atk = r.Next(bossAtkMin, bossAtkMax);playerHp -= atk;//打印信息Console.ForegroundColor = ConsoleColor.Yellow;//先擦除这一行 上次显示的内容Console.SetCursorPosition(2, h - 3);Console.Write("                                           ");//再来写新的信息//boss如果把玩家打死了 做什么if (playerHp <= 0){Console.SetCursorPosition(2, h - 3);Console.Write("很遗憾,你未能通过boss的试炼,战败了");}else{Console.SetCursorPosition(2, h - 3);Console.Write("恶龙对你造成了{0}点伤害,你的剩余血量为{1}", atk, playerHp);}}else{//擦除之前的战斗信息Console.SetCursorPosition(2, h - 5);Console.Write("                                           ");Console.SetCursorPosition(2, h - 4);Console.Write("                                           ");Console.SetCursorPosition(2, h - 3);Console.Write("                                           ");//显示恭喜胜利的信息Console.SetCursorPosition(2, h - 5);Console.Write("你战胜了boss,快去营救公主");Console.SetCursorPosition(2, h - 4);Console.Write("前往公主身边按J键继续");}}}
  • 上面的逻辑我想我已经讲清楚了,现在唯一需要提一点的就是我们每次在打印新的信息时都需要先擦除上一次打印的信息,而由于我们的如果使用Clear清屏的话会把画面上所有内容都给消除,因此我们这里选择在打印新信息前先把对应这一行置为空白,这样一来就能达到我们想要的效果
    在这里插入图片描述
  • 好了,讲到这里,今天有关游戏场景相关的知识也就讲的差不多了,接下来把源码放在这里以供大家参考和自己修改

三. 游戏源码

 //游戏场景case 2:Console.Clear();#region 4 不变的红墙//设置颜色为红色Console.ForegroundColor = ConsoleColor.Red;//画墙//上方墙for (int i = 0; i < w; i += 2){//上方墙Console.SetCursorPosition(i, 0);Console.Write("■");//下方墙Console.SetCursorPosition(i, h - 1);Console.Write("■");//中间墙Console.SetCursorPosition(i, h - 6);Console.Write("■");}//左边的墙for (int i = 0; i < h; i++){//左边的墙Console.SetCursorPosition(0, i);Console.Write("■");//右边的墙Console.SetCursorPosition(w - 2, i);Console.Write("■");}#endregion#region 5 boss属性相关int bossX = 24;int bossY = 15;int bossAtkMin = 7;int bossAtkMax = 13;int bossHp = 100;string bossIcon = "■";//申明一个 颜色变量ConsoleColor bossColor = ConsoleColor.Green;#endregion#region 6 玩家属性相关int playerX = 4;int playerY = 5;int playerAtkMin = 8;int playerAtkMax = 12;int playerHp = 100;string playerIcon = "●";ConsoleColor playerColor = ConsoleColor.Yellow;//玩家输入的内容 外面申明 节约性能char playerInput;#endregion#region 7 玩家战斗相关//战斗状态bool isFight = false;#endregion//游戏场景的死循环 专门用来 检测 玩家输入相关循环while (true){#region 5 boss属性相关//boss活着时才绘制if (bossHp > 0){//绘制boss图标Console.SetCursorPosition(bossX, bossY);Console.ForegroundColor = bossColor;Console.Write(bossIcon);}#endregion#region 6 玩家移动相关//画出玩家Console.SetCursorPosition(playerX, playerY);Console.ForegroundColor = playerColor;Console.Write(playerIcon);//得到玩家输入playerInput = Console.ReadKey(true).KeyChar;#endregion//战斗状态处理什么逻辑if (isFight){#region 7 玩家战斗相关//如果是战斗状态 你做什么//只会处理J键 if (playerInput == 'J' || playerInput == 'j'){//在这判断 玩家或者怪物 是否死亡 如果死亡了 继续之后的流程if (playerHp <= 0){//游戏结束//输掉了 应该直接显示 我们的 游戏结束界面nowSceneID = 3;break;}else if (bossHp <= 0){//去营救公主//boss擦除Console.SetCursorPosition(bossX, bossY);Console.Write("  ");isFight = false;}else{//去处理按J键打架// 玩家打怪物Random r = new Random();//得到随机攻击了int atk = r.Next(playerAtkMin, playerAtkMax);//血量减对应的攻击力bossHp -= atk;//打印信息Console.ForegroundColor = ConsoleColor.Green;//先擦除这一行 上次显示的内容Console.SetCursorPosition(2, h - 4);Console.Write("                                           ");//再来写新的信息Console.SetCursorPosition(2, h - 4);Console.Write("你对boss造成了{0}点伤害,boss剩余血量为{1}", atk, bossHp);// 怪物打玩家if (bossHp > 0){//得到随机攻击了atk = r.Next(bossAtkMin, bossAtkMax);playerHp -= atk;//打印信息Console.ForegroundColor = ConsoleColor.Yellow;//先擦除这一行 上次显示的内容Console.SetCursorPosition(2, h - 3);Console.Write("                                           ");//再来写新的信息//boss如果把玩家打死了 做什么if (playerHp <= 0){Console.SetCursorPosition(2, h - 3);Console.Write("很遗憾,你未能通过boss的试炼,战败了");}else{Console.SetCursorPosition(2, h - 3);Console.Write("boss对你造成了{0}点伤害,你的剩余血量为{1}", atk, playerHp);}}else{//擦除之前的战斗信息Console.SetCursorPosition(2, h - 5);Console.Write("                                           ");Console.SetCursorPosition(2, h - 4);Console.Write("                                           ");Console.SetCursorPosition(2, h - 3);Console.Write("                                           ");//显示恭喜胜利的信息Console.SetCursorPosition(2, h - 5);Console.Write("你战胜了boss,快去营救公主");Console.SetCursorPosition(2, h - 4);Console.Write("前往公主身边按J键继续");}}}#endregion}//非战斗状态处理什么逻辑else{#region 6 玩家移动相关//擦除Console.SetCursorPosition(playerX, playerY);Console.Write("  ");//改位置switch (playerInput){case 'W':case 'w':--playerY;if (playerY < 1){playerY = 1;}//位置如果和boss重合了 并且boss没有死else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去++playerY;}break;case 'A':case 'a':playerX -= 2;if (playerX < 2){playerX = 2;}else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去playerX += 2;}break;case 'S':case 's':++playerY;if (playerY > h - 7){playerY = h - 7;}else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去--playerY;}break;case 'D':case 'd':playerX += 2;if (playerX > w - 4){playerX = w - 4;}else if (playerX == bossX && playerY == bossY && bossHp > 0){//拉回去playerX -= 2;}break;case 'J':case 'j'://开始战斗if ((playerX == bossX && playerY == bossY - 1 ||playerX == bossX && playerY == bossY + 1 ||playerX == bossX - 2 && playerY == bossY ||playerX == bossX + 2 && playerY == bossY) && bossHp > 0){isFight = true;//可以开始战斗Console.SetCursorPosition(2, h - 5);Console.ForegroundColor = ConsoleColor.White;Console.Write("开始和Boss战斗了,按J键继续");Console.SetCursorPosition(2, h - 4);Console.Write("玩家当前血量为{0}", playerHp);Console.SetCursorPosition(2, h - 3);Console.Write("boss当前血量为{0}", bossHp);}//要让玩家不能再移动//下方能够显示信息break;}#endregion}}break;

总结

  • 今天的内容到这里就结束了,游戏场景的相关设置才是这款游戏的核心以及难点,如果你真的想学好这方面的内容不妨跟着博主自己动手尝试一下,毕竟很多问题只有自己动手试过了才能发现,如果你在实现过程中遇到任何问题,欢迎在评论区指出或者私信我!!
  • 后面的内容很快更新,感兴趣不妨关注一下错过后面的内容哦!!

新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下再走呗。你们的支持就是我更新的动力!!!

**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**

在这里插入图片描述

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

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

相关文章

固定资产管理中净值怎么算

在资产管理的领域中&#xff0c;我们经常听到“净值”这个词。然而&#xff0c;对于许多人来说&#xff0c;净值的概念仍然模糊不清。本文将试图揭示固定资产管理的净值计算方法&#xff0c;并提供一些创新的观点。  我们需要明确什么是净值。在财务术语中&#xff0c;净值是…

模拟实现C语言--strlen函数

模拟实现C语言–strlen函数 模拟实现C语言--strlen函数一、strlen函数是什么&#xff1f;二、strlen函数的模拟实现2.1 计数器方式实现strlen函数2.2 不创建临时变量计数器方式实现strlen函数2.3 指针-指针方式实现strlen函数 三、strlen函数的返回类型 一、strlen函数是什么&a…

hive的建表语句

hive建表语句CREATE TABLE ccwn_zh_event_push (customerid string,cardnumber string,accountnumber string,eventcode string,eventtime string,activities string,activityRefuseCode string,lables string)PARTITIONED BY(dt string)ROW FORMAT SERDE org.apache.hadoop.hi…

plt函数显示图片 在图片上画边界框 边界框坐标转换

一.读取图片并显示图片 %matplotlib inline import torch from d2l import torch as d2l读取图片 image_path ../data/images/cat_dog_new.jpg # 创建画板 figure d2l.set_figsize() image d2l.plt.imread(image_path) d2l.plt.imshow(image);二.给出一个(x左上角,y左上角,…

9.12|day 5|day 44 |完全背包| 518. 零钱兑换 II | 377. 组合总和 Ⅳ

● 完全背包 主要是看清01背包和完全背包的区别 //01背包 for(int i 0;i<weight.size();i){ for(int j bagWeight;j>weight[i];j--){dp[j] Math.max(dp[j],dp[j-weight[i]]value[i]); } } //完全背包 for(int i 0;i<weight.size();i){for(int j weight[i];j<…

Java开发之框架(spring、springmvc、springboot、mybatis)【面试篇 完结版】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、框架知识分布二、Spring1. spring-单例bean① 问题引入② 单例bean是线程安全的吗③ 问题总结④ 实战面试 2. spring-AOP① 问题引入② AOP记录操作日志③ …

C++ - map 的 例题

前言 本博客在 一下文章关于 map 和 set 讲解之下&#xff0c;对 map 当中的 operator[] &#xff08;&#xff09;函数的功能运用&#xff0c;感受 map 功能强大。 349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 给定两个数组 nums1 和 nums2 &#xff0c;返回…

9.14号作业

仿照vector手动实现自己的myVector&#xff0c;最主要实现二倍扩容功能 有些功能&#xff0c;不会 #include <iostream>using namespace std; //创建vector类 class Vector { private:int *data;int size;int capacity; public://无参构造Vector(){}//拷贝构造Vector(c…

【C++】常用算术生成算法

0.前言 1.accumulate #include <iostream> using namespace std;// 常用算术生成算法 #include<vector> #include<numeric> //accumulate 的调用头文件void test01() {vector<int>v;for (int i 0; i < 100; i){v.push_back(i);}int total accumu…

【zlm】 webrtc源码讲解

目录 前端WEB 服务器收到请求 服务端的处理 播放 拉流 参考文章 前端WEB 服务器收到请求 POST /index/api/webrtc?applive&streamtest&typeplay HTTP/1.1 HttpSession::onRecvHeaderHttpSession::Handle_Req_POSTHttpSession::Handle_Req_POSTif (totalConte…

Java反射机制简单入门

标题 反射能干嘛获取Class对象的三种方式下面开始重点&#xff0c;需要掌握获取类的构造器并进行操作获取成员变量获取成员方法 这块建议先听第一个视频入门&#xff0c;第二个视频深入了解 视频学习地址1 视频学习地址1 正射:知道某个类&#xff0c;类的地址&#xff0c;通过…

水一下文章

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…