构建可玩关卡与碰撞检测
立即解锁
发布时间: 2025-08-20 01:18:42 阅读量: 1 订阅数: 3 


C++游戏编程入门与实践
### 构建可玩关卡与碰撞检测
在游戏开发中,将关卡设计转化为可玩的游戏关卡是一项重要任务。本文将详细介绍如何通过编码实现关卡的加载和管理,以及如何在游戏引擎中集成这些功能。
#### 1. 构建LevelManager类
要让关卡设计生效,需要分几个阶段进行编码。首先,我们要编写`LevelManager`类的头文件`LevelManager.h`,这样可以查看和讨论该类中的成员变量和函数。
##### 1.1 编码LevelManager.h
操作步骤如下:
1. 在解决方案资源管理器中右键单击“Header Files”,选择“Add | New Item…”。
2. 在“Add New Item”窗口中,左键单击高亮显示“Header File (.h)”,然后在“Name”字段中输入`LevelManager.h`。
3. 点击“Add”按钮。
以下是`LevelManager.h`文件的代码:
```cpp
#pragma once
#include <SFML/Graphics.hpp>
using namespace sf;
using namespace std;
class LevelManager
{
private:
Vector2i m_LevelSize;
Vector2f m_StartPosition;
float m_TimeModifier = 1;
float m_BaseTimeLimit = 0;
int m_CurrentLevel = 0;
const int NUM_LEVELS = 4;
// public declarations go here
public:
const int TILE_SIZE = 50;
const int VERTS_IN_QUAD = 4;
float getTimeLimit();
Vector2f getStartPosition();
int** nextLevel(VertexArray& rVaLevel);
Vector2i getLevelSize();
int getCurrentLevel();
};
```
各成员变量和函数的作用如下:
| 成员 | 类型 | 作用 |
| ---- | ---- | ---- |
| `m_LevelSize` | `Vector2i` | 存储当前地图的水平和垂直瓦片数量 |
| `m_StartPosition` | `Vector2f` | 存储Bob和Thomas的生成坐标(像素位置) |
| `m_TimeModifier` | `float` | 用于乘以当前关卡的可用时间,使游戏难度逐渐增加 |
| `m_BaseTimeLimit` | `float` | 存储原始的、未修改的时间限制 |
| `m_CurrentLevel` | `int` | 存储当前正在玩的关卡编号 |
| `NUM_LEVELS` | `const int` | 关卡总数,用于判断何时回到第一关并减少`m_TimeModifier`的值 |
| `TILE_SIZE` | `const int` | 提醒每个精灵图块的宽度和高度为50像素 |
| `VERTS_IN_QUAD` | `const int` | 方便操作`VertexArray`,一个四边形有四个顶点 |
| `getTimeLimit()` | `float` | 返回当前关卡的时间限制 |
| `getStartPosition()` | `Vector2f` | 返回Bob和Thomas的生成位置 |
| `nextLevel(VertexArray& rVaLevel)` | `int**` | 加载下一个关卡,返回一个二维整数数组的指针 |
| `getLevelSize()` | `Vector2i` | 返回当前关卡的大小 |
| `getCurrentLevel()` | `int` | 返回当前关卡的编号 |
##### 1.2 编码LevelManager.cpp
操作步骤如下:
1. 在解决方案资源管理器中右键单击“Source Files”,选择“Add | New Item…”。
2. 在“Add New Item”窗口中,左键单击高亮显示“C++ File (.cpp)”,然后在“Name”字段中输入`LevelManager.cpp`。
3. 点击“Add”按钮。
由于`LevelManager.cpp`文件较长,我们将其分为六个部分进行讨论,前五个部分涵盖`nextLevel`函数,第六部分涵盖其余函数。
以下是`nextLevel`函数的第一部分代码:
```cpp
#include "stdafx.h"
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include "TextureHolder.h"
#include <sstream>
#include <fstream>
#include "LevelManager.h"
using namespace sf;
using namespace std;
int** LevelManager::nextLevel(VertexArray& rVaLevel)
{
m_LevelSize.x = 0;
m_LevelSize.y = 0;
// Get the next level
m_CurrentLevel++;
if (m_CurrentLevel > NUM_LEVELS)
{
m_CurrentLevel = 1;
m_TimeModifier -= .1f;
}
// Load the appropriate level from a text file
string levelToLoad;
switch (m_CurrentLevel)
{
case 1:
levelToLoad = "levels/level1.txt";
m_StartPosition.x = 100;
m_StartPosition.y = 100;
m_BaseTimeLimit = 30.0f;
break;
case 2:
levelToLoad = "levels/level2.txt";
m_StartPosition.x = 100;
m_StartPosition.y = 3600;
m_BaseTimeLimit = 100.0f;
break;
case 3:
levelToLoad = "levels/level3.txt";
m_StartPosition.x = 1250;
m_StartPosition.y = 0;
m_BaseTimeLimit = 30.0f;
break;
case 4:
levelToLoad = "levels/level4.txt";
m_StartPosition.x = 50;
m_StartPosition.y = 200;
m_BaseTimeLimit = 50.0f;
break;
}// End switch
```
这部分代码的主要逻辑如下:
1. 初始化`m_LevelSize.x`和`m_LevelSize.y`为0。
2. 增加`m_CurrentLevel`的值。
3. 如果`m_CurrentLevel`大于`NUM_LEVELS`,将其重置为1,并减少`m_TimeModifier`的值。
4. 根据`m_CurrentLevel`的值选择要加载的关卡文件,并初始化相应的起始位置和时间限制。
以下是`nextLevel`函数的第二部分代码:
```cpp
ifstream inputFile(levelToLoad);
string s;
// Count the number of rows in the file
while (getline(inputFile, s))
{
++m_LevelSize.y;
}
// Store the length of the rows
m_LevelSize.x = s.length();
```
这部分代码的主要逻辑是:
1. 打开要加载的关卡文件。
2. 逐行读取文件,统计文件的行数,存储在`m_LevelSize.y`中。
3. 存储最后一行的长度,作为关卡的宽度,存储在`m_LevelSize.x`中。
以下是`nextLevel`函数的第三部分代码:
```cpp
// Go back to the start of the file
inputFile.clear();
inputFile.seekg(0, ios::beg);
// Prepare the 2d array to hold the int values from the file
int** arrayLevel = new int*[m_LevelSize.y];
for (int i = 0; i < m_LevelSize.y; ++i)
{
// Add a new array into each array element
arrayLevel[i] = new int[m_LevelSize.x];
}
```
这部分代码的主要逻辑是:
1. 清除文件流的错误标志,并将文件指针重置到文件开头。
2. 在堆上创建一个二维整数数组`arrayLevel`,用于存储关卡文件中的整数数据。
以下是`nextLevel`函数的第四部分代码:
```cpp
// Loop through the file and store all the values in the 2d array
string row;
int y = 0;
while (inputFile >> row)
{
for (int x = 0; x < row.length(); x++) {
const char val = row[x];
arrayLevel[y][x] = atoi(&val);
}
y++;
}
// close the file
inputFile.close();
```
这部分代码的主要逻辑是:
1. 逐行读取文件,将每行的字符数据转换为整数,并存储在`arrayLevel`中。
2. 关闭文件。
以下是`nextLevel`函数的第五部分代码:
```cpp
// What type of primitive are we using?
rVaLevel.setPrimitiveType(Quads);
// Set the size of the vertex array
rVaLevel.resize(m_LevelSize.x * m_LevelSize.y * VERTS_IN_QUAD);
// Start at the beginning of the vertex array
int currentVertex = 0;
for (int x = 0; x < m_LevelSize.x; x++)
{
for (int y = 0; y < m_LevelSize.y; y++)
{
// Position each vertex in the current quad
rVaLevel[currentVertex + 0].position =
Vector2f(x * TILE_SIZE,
y * TILE_SIZE);
rVaLevel[currentVertex + 1].position =
Vector2f((x * TILE_SIZE) + TILE_SIZE,
y * TILE_SIZE);
rVaLevel[currentVertex + 2].position =
Vector2f((x * TILE_SIZE) + TILE_SIZE,
(y * TILE_SIZE) + TILE_SIZE);
rVaLevel[currentVertex + 3].position =
Vector2f((x * TILE_SIZE),
(y * TILE_SIZE) + TILE_SIZE);
// Which tile from the sprite sheet should we use
```
0
0
复制全文
相关推荐










