贪吃蛇的实现以及优化(基于控制台)
文章目录
1.前言
上一个blog我们讲了讲贪吃蛇的核心思想,其实主要就是整个贪吃蛇游戏最核心的部分,有了那些核心思想完成一个简陋的贪吃蛇不成问题。那么如何让简陋的贪吃蛇不再简陋呢?这一篇我们就主要来讲一下如何实现更加完美的贪吃蛇。
2.基于控制台实现。
实验主要的要求有:实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点。“蛇”碰到边界或蛇头与蛇身相撞,蛇死亡,游戏结束。为游戏设计友好的交互界面;例如欢迎界面,游戏界面,游戏结束界面。要有开始键、暂停键和停止退出的选项。对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。
实验要求是需要用到一些交互界面开发软件的。开始的时候自己也是想过要用MFC来实现整个贪吃蛇的,百度了一下相关教程,我所知道的就是B站上只有一篇是讲述MFC实现贪吃蛇的视频。B站上讲解的视频确实讲了很重点的东西,但如果没有任何MFC基础去看那个的话,有点小自闭,很多按键以及添加的头文件和函数都是最大的难题。如果有兴趣的话觉得自己MFC的知识还足够丰富的话,推荐可以去看一下。
我就比较菜了,学了大概一整天,一个多小时的视频看了好几遍,学是没学到多少,整个人是真的自闭了。所以我果断放弃了MFC,就用控制台了。界面丑一点就丑一点了,功能多一点就好了。
3.为贪吃蛇添加更多创新。
(1)输出一个比较漂亮的图像作为墙体
一般来说贪吃蛇的墙都是很普通的“@"或者“#“,就是一个比较短的符号,我们可以在C++里看一下生成地图的效果。
```cpp cout << " "; for (int i=1;i<=n;i++) cout << "-"; cout << endl; for (int j=0;j<=m-1;j++) { cout << "|"; for (int i=1;i<=n;i++) cout << " "; cout << "|" << endl; } cout << " "; for (int i=1;i<=n;i++)
cout <span class="token operator"><<</span> <span class="token string">"-"</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
大概是这样的:
还是比较丑的,这时候我们能不能用特殊符号来当作墙呢,输出的话应该就比较漂亮一点了。就比如▲或◆或●这样的。但是这时候如果我们还是像上次那样定位然后输出的话可以试下效果
cout << " ";
for (int i=1;i<=n;i++)
cout << "■";
cout << endl;
for (int j=0;j<=m-1;j++)
{
cout << "■";
for (int i=1;i<=n;i++) cout << " ";
cout << "■" << endl;
}
cout << " ";
for (int i=1;i<=n;i++)
cout << "■";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
有点奇怪,为什么呢,这就要说明一下了,我们#$%这种符号都占一个字节,而在屏幕上你会发现屏幕的宽两个符号之间距离特别大,具体应该是宽是长的两倍,宽就占了大概两个字节。所以我们用特殊符号其实输出的不是一个字节的数,而是一个两字节的符号。我们刚刚输出的空格其实算是一个字节的,这时候只需要输出两个空格就好了。
这样看起来就舒服多了,但其实还是有问题的。我们所用到的蛇还是只占一个字节的,所以我们还必须改一下蛇占数组长度的值,把列数从1改为2.
(2)初始化改变颜色,以及窗口大小。
对于想让用户自定义颜色的问题,需要用到的函数其实也是包含在头文件下,就是windows.h这个头文件。实现改变颜色的话,我们需要用到system函数分别进行几个操作。system(”cls“),system(”pause“),system(“color. XX”),实现清屏功能,暂停功能以及改变前景色和背景色的功能。基本思想就是用户输入两个字符,然后判断这两个字符是不是十六进制中的一个(0~A),如果是的话就可以调用system函数。然后判断用户按键输入的字符是什么,然后调用System(“color XX”)函数,XX就可以赋值为用户所输入的那个值。实现起来的话,不嫌麻烦的话可以直接switch嵌套switc case,然后得到第一个十六进制数,再得到另一个十六进制的数字。
#include<Windows.h>
void color(char a, char b)
{
switch (a)
{
case '0':
switch (b) {
case'0':
system("color 00"); break;
case'1':
system("color 01"); break;
case'2':
system("color 02"); break;
case'3':
system("color 03"); break;
case'4':
system("color 04"); break;
case'5':
system("color 05"); break;
case'6':
system("color 06"); break;
case'7':
system("color 07"); break;
case'8':
system("color 08"); break;
case'9':
system("color 09"); break;
case'A':
system("color 0A"); break;
case'B':
system("color 0B"); break;
case'C':
system("color 0C"); break;
case'D':
system("color 0D"); break;
case'E':
system("color 0E"); break;
case'F':
system("color 0F"); break;
}
break;
case '1':
switch (b) {
case'0':
system("color 10"); break;
case'1':
system("color 11"); break;
case'2':
system("color 12"); break;
case'3':
system("color 13"); break;
case'4':
system("color 14"); break;
case'5':
system("color 15"); break;
case'6':
system("color 16"); break;
case'7':
system("color 17"); break;
case'8':
system("color 18"); break;
case'9':
system("color 19"); break;
case'A':
system("color 1A"); break;
case'B':
system("color 1B"); break;
case'C':
system("color 1C"); break;
case'D':
system("color 1D"); break;
case'E':
system("color 1E"); break;
case'F':
system("color 1F"); break;
}
break;
case '2':
switch (b) {
case'0':
system("color 20"); break;
case'1':
system("color 21"); break;
case'2':
system("color 22"); break;
case'3':
system("color 23"); break;
case'4':
system("color 24"); break;
case'5':
system("color 25"); break;
case'6':
system("color 26"); break;
case'7':
system("color 27"); break;
case'8':
system("color 28"); break;
case'9':
system("color 29"); break;
case'A':
system("color 2A"); break;
case'B':
system("color 2B"); break;
case'C':
system("color 2C"); break;
case'D':
system("color 2D"); break;
case'E':
system("color 2E"); break;
case'F':
system("color 2F"); break;
}
break;
case '3':
switch (b) {
case'0':
system("color 30"); break;
case'1':
system("color 31"); break;
case'2':
system("color 32"); break;
case'3':
system("color 33"); break;
case'4':
system("color 34"); break;
case'5':
system("color 35"); break;
case'6':
system("color 36"); break;
case'7':
system("color 37"); break;
case'8':
system("color 38"); break;
case'9':
system("color 39"); break;
case'A':
system("color 3A"); break;
case'B':
system("color 3B"); break;
case'C':
system("color 3C"); break;
case'D':
system("color 3D"); break;
case'E':
system("color 3E"); break;
case'F':
system("color 3F"); break;
}
break;
case '4':
switch (b) {
case'0':
system("color 40"); break;
case'1':
system("color 41"); break;
case'2':
system("color 42"); break;
case'3':
system("color 43"); break;
case'4':
system("color 44"); break;
case'5':
system("color 45"); break;
case'6':
system("color 46"); break;
case'7':
system("color 47"); break;
case'8':
system("color 48"); break;
case'9':
system("color 49"); break;
case'A':
system("color 4A"); break;
case'B':
system("color 4B"); break;
case'C':
system("color 4C"); break;
case'D':
system("color 4D"); break;
case'E':
system("color 4E"); break;
case'F':
system("color 4F"); break;
}
break;
case '5':
switch (b) {
case'0':
system("color 50"); break;
case'1':
system("color 51"); break;
case'2':
system("color 52"); break;
case'3':
system("color 53"); break;
case'4':
system("color 54"); break;
case'5':
system("color 55"); break;
case'6':
system("color 56"); break;
case'7':
system("color 57"); break;
case'8':
system("color 58"); break;
case'9':
system("color 59"); break;
case'A':
system("color 5A"); break;
case'B':
system("color 5B"); break;
case'C':
system("color 5C"); break;
case'D':
system("color 5D"); break;
case'E':
system("color 5E"); break;
case'F':
system("color 5F"); break;
}
break;
case '6':
switch (b) {
case'0':
system("color 60"); break;
case'1':
system("color 61"); break;
case'2':
system("color 62"); break;
case'3':
system("color 63"); break;
case'4':
system("color 64"); break;
case'5':
system("color 65"); break;
case'6':
system("color 66"); break;
case'7':
system("color 67"); break;
case'8':
system("color 68"); break;
case'9':
system("color 69"); break;
case'A':
system("color 6A"); break;
case'B':
system("color 6B"); break;
case'C':
system("color 6C"); break;
case'D':
system("color 6D"); break;
case'E':
system("color 6E"); break;
case'F':
system("color 6F"); break;
}
break;
case '7':
switch (b) {
case'0':
system("color 70"); break;
case'1':
system("color 71"); break;
case'2':
system("color 72"); break;
case'3':
system("color 73"); break;
case'4':
system("color 74"); break;
case'5':
system("color 75"); break;
case'6':
system("color 76"); break;
case'7':
system("color 77"); break;
case'8':
system("color 78"); break;
case'9':
system("color 79"); break;
case'A':
system("color 7A"); break;
case'B':
system("color 7B"); break;
case'C':
system("color 7C"); break;
case'D':
system("color 7D"); break;
case'E':
system("color 7E"); break;
case'F':
system("color 7F"); break;
}
break;
case '8':
switch (b) {
case'0':
system("color 80"); break;
case'1':
system("color 81"); break;
case'2':
system("color 82"); break;
case'3':
system("color 83"); break;
case'4':
system("color 84"); break;
case'5':
system("color 85"); break;
case'6':
system("color 86"); break;
case'7':
system("color 87"); break;
case'8':
system("color 88"); break;
case'9':
syqostem("color 89"); break;
case'A':
system("color 8A"); break;
case'B':
system("color 8B"); break;
case'C':
system("color 8C"); break;
case'D':
system("color 8D"); break;
case'E':
system("color 8E"); break;
case'F':
system("color 8F"); break;
}
break;
case '9':
switch (b) {
case'0':
system("color 90"); break;
case'1':
system("color 91"); break;
case'2':
system("color 92"); break;
case'3':
system("color 93"); break;
case'4':
system("color 94"); break;
case'5':
system("color 95"); break;
case'6':
system("color 96"); break;
case'7':
system("color 97"); break;
case'8':
system("color 98"); break;
case'9':
system("color 99"); break;
case'A':
system("color 9A"); break;
case'B':
system("color 9B"); break;
case'C':
system("color 9C"); break;
case'D':
system("color 9D"); break;
case'E':
system("color 9E"); break;
case'F':
system("color 9F"); break;
}
break;
case 'A':
switch (b) {
case'0':
system("color A0"); break;
case'1':
system("color A1"); break;
case'2':
system("color A2"); break;
case'3':
system("color A3"); break;
case'4':
system("color A4"); break;
case'5':
system("color A5"); break;
case'6':
system("color A6"); break;
case'7':
system("color A7"); break;
case'8':
system("color A8"); break;
case'9':
system("color A9"); break;
case'A':
system("color AA"); break;
case'B':
system("color AB"); break;
case'C':
system("color AC"); break;
case'D':
system("color AD"); break;
case'E':
system("color AE"); break;
case'F':
system("color AF"); break;
}
break;
case 'B':
switch (b) {
case'0':
system("color B0"); break;
case'1':
system("color B1"); break;
case'2':
system("color B2"); break;
case'3':
system("color B3"); break;
case'4':
system("color B4"); break;
case'5':
system("color B5"); break;
case'6':
system("color B6"); break;
case'7':
system("color B7"); break;
case'8':
system("color B8"); break;
case'9':
system("color B9"); break;
case'A':
system("color BA"); break;
case'B':
system("color BB"); break;
case'C':
system("color BC"); break;
case'D':
system("color BD"); break;
case'E':
system("color BE"); break;
case'F':
system("color BF"); break;
}
break;
case 'C':
switch (b) {
case'0':
system("color C0"); break;
case'1':
system("color C1"); break;
case'2':
system("color C2"); break;
case'3':
system("color C3"); break;
case'4':
system("color C4"); break;
case'5':
system("color C5"); break;
case'6':
system("color C6"); break;
case'7':
system("color C7"); break;
case'8':
system("color C8"); break;
case'9':
system("color C9"); break;
case'A':
system("color CA"); break;
case'B':
system("color CB"); break;
case'C':
system("color CC"); break;
case'D':
system("color CD"); break;
case'E':
system("color CE"); break;
case'F':
system("color CF"); break;
}
break;
case 'D':
switch (b) {
case'0':
system("color D0"); break;
case'1':
system("color D1"); break;
case'2':
system("color D2"); break;
case'3':
system("color D3"); break;
case'4':
system("color D4"); break;
case'5':
system("color D5"); break;
case'6':
system("color D6"); break;
case'7':
system("color D7"); break;
case'8':
system("color D8"); break;
case'9':
system("color D9"); break;
case'A':
system("color DA"); break;
case'B':
system("color DB"); break;
case'C':
system("color DC"); break;
case'D':
system("color DD"); break;
case'E':
system("color DE"); break;
case'F':
system("color DF"); break;
}
break;
case 'E':
switch (b) {
case'0':
system("color E0"); break;
case'1':
system("color E1"); break;
case'2':
system("color E2"); break;
case'3':
system("color E3"); break;
case'4':
system("color E4"); break;
case'5':
system("color E5"); break;
case'6':
system("color E6"); break;
case'7':
system("color E7"); break;
case'8':
system("color E8"); break;
case'9':
system("color E9"); break;
case'A':
system("color EA"); break;
case'B':
system("color EB"); break;
case'C':
system("color EC"); break;
case'D':
system("color ED"); break;
case'E':
system("color EE"); break;
case'F':
system("color EF"); break;
}
break;
case'F':
switch (b) {
case'0':
system("color F0"); break;
case'1':
system("color F1"); break;
case'2':
system("color F2"); break;
case'3':
system("color F3"); break;
case'4':
system("color F4"); break;
case'5':
system("color F5"); break;
case'6':
system("color F6"); break;
case'7':
system("color F7"); break;
case'8':
system("color F8"); break;
case'9':
system("color F9"); break;
case'A':
system("color FA"); break;
case'B':
system("color FB"); break;
case'C':
system("color FC"); break;
case'D':
system("color FD"); break;
case'E':
system("color FE"); break;
case'F':
system("color FF"); break;
}
break;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
的确是比较多,也比较笨吧,但至少可以写出来。
关于窗口大小的语句,其实也是为了让地图可以设置的更大一点,不用每一次都麻烦地去调一下最大化,在这个过程之中很可能会出现BUG这类的,所以就直接窗口最大化吧。相关的窗口设定大小百度应该有具体教程,讲解地比较详细。我这里就说一下大概,就是读取电脑屏幕的大小用像素记录一下,然后用设置窗口大小的函数来设置一下程序输出有多大。
void full_screen() { HWND hwnd = GetForegroundWindow(); int cx = GetSystemMetrics(SM_CXSCREEN); /* 屏幕宽度 像素 */ int cy = GetSystemMetrics(SM_CYSCREEN); /* 屏幕高度 像素 */ LONG l_WinStyle = GetWindowLong(hwnd, GWL_STYLE); /* 获取窗口信息 */ /* 设置窗口信息 最大化 取消标题栏及边框 */ SetWindowLong(hwnd, GWL_STYLE, (l_WinStyle | WS_POPUP | WS_MAXIMIZE) & ~WS_CAPTION & ~WS_THICKFRAME & ~WS_BORDER);
<span class="token function">SetWindowPos</span><span class="token punctuation">(</span>hwnd<span class="token punctuation">,</span> HWND_TOP<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> cx<span class="token punctuation">,</span> cy<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
(3)更具想象力的贪吃蛇功能
关于这点,可以实现好多,我们可以吃不同的食物增加不同的长度,也可以吃一次食物改变一次颜色,也可以在地图上随机生成障碍物,蛇碰到会死亡。还有好多具有特色的功能可以去实现。
关于生成不同分数种类的食物,我所想到的方法是先生成一个食物坐标点,先把这个点确定了再说具体生成的类型。具体生成类型的函数也可以用一个随时间变化的函数来判断,假定T=1,2,3。1生成一种食物,2生成另一种,3生成下一种。
bool print_food()
{
srand((unsigned)time(0));//srand是种子函数,需要unsinged类型的输入,unsigned函数是为了将系统的时间time类型转换为unsigned类型。time函数赋值为0,是获取标准时间函数
bool e;
while (1)//循环恒成立,除非break跳出循环结构。
{
e = true;//将bool e赋值为真。
int i = (int)(random((double)H+1, (double)m + H));
int j=(int)(random((double)L+1, (double)n + L ));//随机生成食物的x,y坐标。
food.x = i; food.y = j;
for (int k = 0; k <= snake_length - 1; k++)//判断食物生成的位置是不是位于蛇的身体上,如果不是跳出循环,并且将食物的标志e设置为真,反之设置为假。
{
if (snake[k].x == food.x && snake[k].y == food.y)
{
e = false; break;
}
}
if (e) break; //判断食物生成的位置不在蛇的身体上就跳出循环。
}
locate(food.x, food.y);//在指定的位置上生成食物。
srand((unsigned)time(NULL));
int T;
T = rand() % 3 + 1;
if (T == 1) {
cout << "¥";
food1 = food;
return false;
}//食物1的标志为¥,吃到这食物蛇的长度增加1。
else {
if (T == 2) {
cout << "▲";//食物2的标志位▲,吃到这个食物的长度增加2。
food2 = food;
return false;
}
else {
if (T == 3) {
cout << "★";//食物3的标志为★,吃到这个食物蛇的长度增加3。
food3 = food;
return false;
}
}
}
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
这上面只是一个简单的例子,吃到食物长度增加多少,甚至还可以减少,都是自己去定义的,反正原理是一样的。
关于变色的实现,就更为简单了,我们可以判定分数达到一定的值会变色,吃到特定的食物会变色,随着时间的推移会自动变色等等之类的。或者是蛇身体可以有不同的花色,这些都是可以实现的。也是举个简单的例子,比如达到多少分会变色。
int score(int a) {//计算得分的函数。
a = a - 3;//先把初始的长度减去。
int b = 0; //定义这个函数的返回值为b。
if (a != 0) {
if (a > 0 && a <= 10) b = a * 5;
else {
if (a > 50) b = 450 + a * 50;//得分计算的方式
else b = 50 + a * 10;
}
}
if (b >= 100&&b<=200) system("color 4C");
if (b > 200 && b <= 300)system("color 3A");
if (b > 300 && b <= 400)system("color 2F");
if (b > 400)system("color 8B");
return b;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
然后讲解一下有关其他功能实现的大概思路,比如实现穿墙,我们不能简单地判断蛇撞墙不会死亡,这样的程序不能用。实现穿墙必须蛇从下面的墙进去,然后从上面的相同位置按照相同长度穿出来。这个在我的程序里是用到了一个定位函数,可以判定蛇头碰到墙体然后对应碰到上墙体或者下墙体的话,它的列数是不变的当行数需要从上(下)墙体转变为下(上)墙体的坐标。相应的,左右是行数不变,列数改变。根据上一篇讲解的,我的程序是dir控制方向的全局变量,这个方向也不变,用定位函数确定了第一个蛇体的位置之后,还是按照原则,根据蛇链表长度,把蛇打印出来就好了,相当于一次初始化。
还有一个比较重要的点,随着蛇身体长度的增加,应该游戏是越来越难的,并且初始化的时候还可以选择难度,这是需要的函数其实是计时函数clock,只需要控制两个计时器之间时间的差值,然后经这一段时间之后,蛇可以前进,因为clock的时间单位是ms,所以我们一定要慎重判断这个参数的设定。我对于这些参数没什么概念,最后还是参照了一些有关资料吧。
while (1)
{
/*难度随长度增加而提高*/
hard_len = (double)snake_length / (double)(double(m) * double(n));
/*调节时间,单位是ms*/
a = clock();
while (1)//循环恒成立。
{
b = clock();
if ((b - a )>= (int)((400 - 30 * hard) * (1 - sqrt(hard_len)))) break;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
讲解一下是什么意思,计算方法中的30hard就是我们刚开始输入的难度,sqrt是开方,hard_len其实就是蛇的长度除以地图的大小,毕竟不同地图的不可能用到同一个参数。比如难度我们设置了1~10中的5,400-530就等于250,比如地图大小为10乘以10,蛇的长度为25,此时另一个值就为25/100=1/4,然后开根,就为1/2。两个数字相乘,1/2乘以250=125ms,所以每一次相隔125ms蛇就会移动一次。
如果还想有更高级的功能的话,就需要用到进阶版的AI贪吃蛇,需要用到下一篇博客讲解的A*算法,那就下一节再次讲解。
(4)实现简单的交互界面以及功能
要有一个比较完善的交互界面和初始化功能,比如失败了,还可以重新开始,或者刚开始输入参数的话,我们可以用一步一步的引导来让用户输入参数。此时最重要的就是背景颜色和文字在地图中的位置了。关于这点还是用到了最为关键的定位函数。
/*下面这一部分是控制台里的语句,需要我们自己去学习一些相关知识。 */
/光标读取/
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输入输出的句柄,这个函数是在windows.h头文件下的属于控制台函数。
COORD coord;//这是获取坐标的结构体的名称。
void locate(int x, int y)
{
COORD coord;
coord.X = y * 2;
coord.Y = x;
//获取控制台缓冲区句柄
HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
//设置光标位置
SetConsoleCursorPosition(ConsoleHandle, coord);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
上面的定位函数需要解释的就两点,其他的话都是一些基本的控制台操作,相关知识需要自己学习。第一点为什么X得到是y,Y得到的是x,这个是因为我的坐标系选取的是以屏幕的左上角为坐标原点,横向为x轴,纵向为y轴,与电脑函数中的行和列正好是坐标互换过了的。为什么*2其实上面解释过了,一般显示的话我们可以明显看出来横向显示的话,是挨得比较紧的,而纵向显示的话是比较稀疏的,这就是因为行数所占宽度是列数的两倍,我们要用两倍所占的列数来显示特殊符号,他所占的字符就为两个字符,要确保它为正方形。
那就简单展示一下我设计的图形吧!
死亡之后可以重新开始的话,可以之直接在死亡之后加一个按键输入,输入“y”重新开始,输入“n”退出,重新开始的话判断输入y直接调用初始化函数就可以了。
if (_kbhit())//kbhit()检测是否有按键按下,有按下返回键值。
{
ch = _getch();
if (ch == 'y' || ch == 'Y')
{
system("cls");
initall();
break;
}
if (ch == 'N' || ch == 'n')
{
locate(31, 50);
cout << "游戏结束!!!" << endl;
break;
}
system("pause");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
还有一点暂停功能的补充吧,就是空格暂停,然后再次按下控制就继续。其实也就是在按键那里加一个判定吧,就是如果按下空格键的话,只有按下下一个空格的话才可以继续读取按键。
if (_kbhit())//kbhit()检测是否有按键按下,有按下返回键值。
{
ch = _getch();
if (ch == ' ')//暂停的功能实现
{
while (_getch() != ' ') { //再次按下空格继续。
ch = _getch();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
加上上篇博客的所有,我们就大概实现了几乎整个功能吧,也让很简陋的贪吃蛇有了一点自己的特色功能吧,虽然还是很简陋。
下面附一下所有程序的代码吧!color函数放在头文件里了。include一下就好了。
#include<bits/stdc++.h>
#include<ctime>
#include<conio.h>
#include<windows.h>
#include"color.h"
using namespace std;
const int L = 10;// 生成蛇的列位置为居中
const int H = 2;//生成蛇的位置行为居中
/*定义地图的长宽,蛇的坐标,长度,方向,食物的位置。*/
int m, n, end1;
struct node//定义节点的结构体,主要是为了蛇身体的表示和随机生成食物。
{
int x, y;
}
snake[1000];
int snake_length, dir;//定义蛇的长度以及判断方向的参数。
node food, food1, food2, food3;//定义食物
int direct[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };//定义移动方向所对应的数组的操作,四行两列的数组。
/*数据初始化,包括蛇的长度,蛇的位置,方向*/
clock_t a, b;//判断难度的参数。
char ch;//存放键盘输入的按键。
double hard_len;//设置难度。
void full_screen();
int hard;
int map1[5000][5000];
char A, B;
using namespace std;
void locate(int, int);
void hide();
double random(double, double);
int score(int);
void print_snake();
bool is_correct();
bool print_food();
bool go_ahead();
void print_wall(int, int);
void initall();
/*下面这一部分是控制台里的语句,需要我们自己去学习一些相关知识。 */
/光标读取/
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输入输出的句柄,这个函数是在windows.h头文件下的属于控制台函数。
COORD coord;//这是获取坐标的结构体的名称。
void locate(int x, int y)
{
COORD coord;
coord.X = y 2; //第3列
coord.Y = x; //第3行
//获取控制台缓冲区句柄
HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
//设置光标位置
SetConsoleCursorPosition(ConsoleHandle, coord);
}
/隐藏光标/
void hide() {
CONSOLE_CURSOR_INFO cursor_info = {
1,0 };//函数结构体,赋值为0,光标被隐藏。
/CONSOLE_CURSOR_INFO结构体定义如下:
typedef struct
{ DWORD dwSize;
BOOL bVisible; //为0时光标不可见
CONSOLE_CURSOR_INFO, PCONSOLE_CURSOR_INFO;
}
/
SetConsoleCursorInfo(hout, &cursor_info);//函数 SetConsoleCursorInfo 设置光标的大小和可见度。需向其传递指向结构 CONSOLE_CURSOR_INFO 的指针:
}
/生成随机数/
double random(double start, double end)
{
return start + (end - start) rand() / (RAND_MAX + 1.0);//主要是为了确保生成的随机数在固定的范围内,并且要确保随机数生成的随机性。
}
// -1,0 代表向上移动过,1,0为向下移动。0,-1为向左移动。0,1为向右移动。
int score(int a) {
//计算得分的函数。
a = a - 3;//先把初始的长度减去。
int b = 0; //定义这个函数的返回值为b。
if (a != 0) {
if (a > 0 && a <= 10) b = a 5;
else {
if (a > 50) b = 450 + a 50;//得分计算的方式
else b = 50 + a 10;
}
}
if (b >= 100&&b<=200) system(“color 4C”);
if (b > 200 && b <= 300)system(“color 3A”);
if (b > 300 && b <= 400)system(“color 2F”);
if (b > 400)system(“color 8B”);
return b;
}
/首次输出蛇头,snake[0]代表蛇头/
void print_snake()
{
locate(snake[0].x, snake[0].y);//确定蛇头在数组里的位置。
printf(“●”);//蛇头的形状为●。
for (int i = 1; i <= snake_length - 1; i++)//判断蛇的身体有多长。
{
locate(snake[i].x, snake[i].y);//确定每一节蛇身的位置。
printf(“◆”);//打印蛇身的形状为◆。
}
}
/判断蛇头是否撞到墙壁或者撞到自己的身体/
bool is_correct()//返回值为假的时候说明撞到了蛇身或者墙壁,为真说明没有撞到 。
{
if (snake[0].x H || snake[0].y L || snake[0].x m + H || snake[0].y n + L) return false;//判断是否撞墙。
for (int i = 1; i <= snake_length - 1; i++)
{
if (snake[0].x snake[i].x && snake[0].y snake[i].y) return false;//判断是否撞到了蛇身。
}
return true; //如果都没有撞到,返回值为真。
}
/运用种子函数来随机生成并输出食物位置/
bool print_food()
{
srand((unsigned)time(0));//srand是种子函数,需要unsinged类型的输入,unsigned函数是为了将系统的时间time类型转换为unsigned类型。time函数赋值为0,是获取标准时间函数
bool e;
while (1)//循环恒成立,除非break跳出循环结构。
{
e = true;//将bool e赋值为真。
int i = (int)(random((double)H+1, (double)m + H));
int j=(int)(random((double)L+1, (double)n + L ));//随机生成食物的x,y坐标。
food.x = i; food.y = j;
for (int k = 0; k <= snake_length - 1; k++)//判断食物生成的位置是不是位于蛇的身体上,如果不是跳出循环,并且将食物的标志e设置为真,反之设置为假。
{
if (snake[k].x food.x && snake[k].y food.y)
{
e = false; break;
}
}
if (e) break; //判断食物生成的位置不在蛇的身体上就跳出循环。
}
locate(food.x, food.y);//在指定的位置上生成食物。
srand((unsigned)time(NULL));
int T;
T = rand() % 3 + 1;
if (T 1) {
cout << “¥”;
food1 = food;
return false;
}//食物1的标志为¥,吃到这食物蛇的长度增加1。
else {
if (T 2) {
cout << “▲”;//食物2的标志位▲,吃到这个食物的长度增加2。
food2 = food;
return false;
}
else {
if (T == 3) {
cout << “★”;//食物3的标志为★,吃到这个食物蛇的长度增加3。
food3 = food;
return false;
}
}
}
return true;
}
/蛇的前进/
bool go_ahead()
{
node temp;//定义蛇身的节点当作中间变量。
bool e = false;
temp = snake[snake_length - 1];//把包括蛇头的完整蛇的长度赋值给temp。
for (int i = snake_length - 1; i >= 1; i–)
snake[i] = snake[i - 1];//把蛇头开始的数值全部赋值给蛇身,确定所有点的值,这些值存在的地方就是要打印蛇体的地方。
snake[0].x += direct[dir][0];//蛇的横向移动。
snake[0].y += direct[dir][1];//蛇的纵向移动。
locate(snake[1].x, snake[1].y);//确定蛇身所占的全部数组。
printf(“◆”);//输出所有的蛇身
<span class="token keyword">if</span> <span class="token punctuation">(</span>snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>x <span class="token operator">==</span> food1<span class="token punctuation">.</span>x <span class="token operator">&&</span> snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>y <span class="token operator">==</span> food1<span class="token punctuation">.</span>y<span class="token punctuation">)</span><span class="token comment">//蛇吃到了食物。 </span>
<span class="token punctuation">{<!-- --></span>
snake_length<span class="token operator">++</span><span class="token punctuation">;</span><span class="token comment">//蛇身的长度加一。 </span>
e <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><span class="token comment">//将食物的标志位设置为真。 </span>
snake<span class="token punctuation">[</span>snake_length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> temp<span class="token punctuation">;</span><span class="token comment">//把原来蛇总体长度temp的值赋给现在吃到食物之后的蛇身数组。 </span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>x <span class="token operator">==</span> food2<span class="token punctuation">.</span>x <span class="token operator">&&</span> snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>y <span class="token operator">==</span> food2<span class="token punctuation">.</span>y<span class="token punctuation">)</span><span class="token comment">//蛇吃到了食物。 </span>
<span class="token punctuation">{<!-- --></span>
snake_length <span class="token operator">+</span><span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span><span class="token comment">//蛇身的长度加二。 </span>
e <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><span class="token comment">//将食物的标志位设置为真。 </span>
snake<span class="token punctuation">[</span>snake_length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> temp<span class="token punctuation">;</span><span class="token comment">//把原来蛇总体长度temp的值赋给现在吃到食物之后的蛇身数组。 </span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>x <span class="token operator">==</span> food3<span class="token punctuation">.</span>x <span class="token operator">&&</span> snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>y <span class="token operator">==</span> food3<span class="token punctuation">.</span>y<span class="token punctuation">)</span><span class="token comment">//蛇吃到了食物。 </span>
<span class="token punctuation">{<!-- --></span>
snake_length <span class="token operator">+</span><span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span><span class="token comment">//蛇身的长度加三。 </span>
e <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><span class="token comment">//将食物的标志位设置为真。 </span>
snake<span class="token punctuation">[</span>snake_length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> temp<span class="token punctuation">;</span><span class="token comment">//把原来蛇总体长度temp的值赋给现在吃到食物之后的蛇身数组。 </span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>e<span class="token punctuation">)</span><span class="token comment">//没吃到食物标志位依旧为假,非假为真。 </span>
<span class="token punctuation">{<!-- --></span>
<span class="token function">locate</span><span class="token punctuation">(</span>temp<span class="token punctuation">.</span>x<span class="token punctuation">,</span> temp<span class="token punctuation">.</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//确定蛇身最后一个数组的点,将值赋为空,如果吃到了食物,长度加一,所以末尾不需要发生变化。 </span>
cout <span class="token operator"><<</span> <span class="token string">" "</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">else</span>
<span class="token function">print_food</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//蛇吃到了食物之后,食物才会继续生成。 </span>
<span class="token function">locate</span><span class="token punctuation">(</span>snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>x<span class="token punctuation">,</span> snake<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//最后确定蛇头的位置,输出蛇头。 </span>
<span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"●"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">is_correct</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">//这个函数前面设定了内容,为真说明什么都没撞到,为假执行下面的语句。 </span>
<span class="token punctuation">{<!-- --></span>
<span class="token function">system</span><span class="token punctuation">(</span><span class="token string">"cls"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">system</span><span class="token punctuation">(</span><span class="token string">"color F4"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">locate</span><span class="token punctuation">(</span><span class="token number">30</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>snake_length <span class="token operator">==</span> end1<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
cout <span class="token operator"><<</span> <span class="token string">"干得漂亮,你成功了"</span><span class="token punctuation">;</span>
<span class="token function">locate</span><span class="token punctuation">(</span><span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
cout <span class="token operator"><<</span> <span class="token string">"你最后的长度为:"</span> <span class="token operator"><<</span> snake_length<span class="token punctuation">;</span>
<span class="token function">locate</span><span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
cout <span class="token operator"><<</span> <span class="token string">"你的得分为:"</span> <span class="token operator"><<</span> <span class="token function">score</span><span class="token punctuation">(</span>snake_length<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">locate</span><span class="token punctuation">(</span><span class="token number">33</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
cout <span class="token operator"><<</span> <span class="token string">"输入y继续,输入n退出"</span> <span class="token operator"><<</span> endl<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
cout <span class="token operator"><<</span> <span class="token string">"你失败了!"</span><span class="token punctuation">;</span>
<span class="token function">locate</span><span class="token punctuation">(</span><span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
cout <span class="token operator"><<</span> <span class="token string">"你最后的长度为:"</span> <span class="token operator"><<</span> snake_length<span class="token punctuation">;</span>
<span class="token function">locate</span><span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
cout <span class="token operator"><<</span> <span class="token string">"你的得分为:"</span> <span class="token operator"><<</span> <span class="token function">score</span><span class="token punctuation">(</span>snake_length<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">locate</span><span class="token punctuation">(</span><span class="token number">33</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
cout <span class="token operator"><<</span> <span class="token string">"输入y继续,输入n退出"</span> <span class="token operator"><<</span> endl<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
}
void print_wall(int m, int n) {
//用数组来输出整个墙
int a, b;
a = m + H;
b = n + L;
for (int i = 0; i < m+H; i++) {
for (int j = 0; j < n+L; j++) {
map1[i][j] = 0;
}
}
for (int i = H; i <= a; i++) {
map1[i][L] = 1;
map1[i][b] = 1;//第一列和最后一列都赋值为1.
}
for (int i = L; i <= b; i++) {
map1[H][i] = 1;
map1[a][i] = 1;
}
for (int i = 0; i <= a; i++) {
for (int j = 0; j <= b; j++) {
if (map1[i][j] 0)printf(" ");
if (map1[i][j] 1)printf(“■”);
}
printf("\n");
}
}
void full_screen()
{
HWND hwnd = GetForegroundWindow()</s