简述
sokoban-cocos2dx
此版本为推箱子游戏的基础版本, 后续添加如下功能
- 人物动画
- TiledMap 解析
- 射线碰撞检测
- 下一步提示, C++算法解析
- 道具, 可以回退一步
源码运行方式
通过 cocos
命令新建一个项目, 将本项目的 Reources
目录和 Classes
目录复制过去即可
目前实现方式
使用图集绘制 Sprite , 然后手动处理碰撞检测
关于推箱子
游戏学论著翻译:《人生必玩的1001个游戏》0044 《推箱子》
【GM】火遍全球的推箱子,那些你所不知道的故事!
【GM】我采访到了推箱子的原作者!仓库番的发展史(续)
cocos2d-x 版本
3.17.2
一个在线推箱子游戏
https://www.jxgame.net/
https://www.12357.net/
素材来源
https://download.csdn.net/download/qq_40535641/11156306
开发过程
添加素材
测试素材是否可用
第一版简单起见, 直接将所有的 Sprite 的 ContentSize 设置为 48x48
点击查看代码
bool HelloWorld::init()
{
// 先执行父类的 init
if (!Scene::init())
{
return false;
}
// 获得可视区域大小
const auto visibleSize = Director::getInstance()->getVisibleSize();
log("width:%f , height:%f", visibleSize.width, visibleSize.height);
// 获得原点位置 左下角
const Vec2 origin = Director::getInstance()->getVisibleOrigin();
log("x:%f , y:%f", origin.x, origin.y);
auto spritecache = SpriteFrameCache::getInstance();
spritecache->addSpriteFramesWithFile("texture_packer/sprite.plist", "texture_packer/sprite.png");
auto wall = Sprite::createWithSpriteFrameName("wall.png");
wall->setContentSize(Size(48, 48));
wall->setPosition(Vec2(200 + 48 * 0, 200));
this->addChild(wall, 1);
auto box_blue = Sprite::createWithSpriteFrameName("box_blue.png");
box_blue->setContentSize(Size(48, 48));
box_blue->setPosition(Vec2(200 + 48 * 1 + 1, 200));
this->addChild(box_blue, 1);
auto box_red = Sprite::createWithSpriteFrameName("box_red.png");
box_red->setContentSize(Size(48, 48));
box_red->setPosition(Vec2(200 + 48 * 2 + 2, 200));
this->addChild(box_red, 1);
auto ball_blue = Sprite::createWithSpriteFrameName("ball_blue.png");
ball_blue->setContentSize(Size(48, 48));
ball_blue->setPosition(Vec2(200 + 48 * 3 + 1, 200));
this->addChild(ball_blue, 1);
auto ball_red = Sprite::createWithSpriteFrameName("ball_red.png");
ball_red->setContentSize(Size(48, 48));
ball_red->setPosition(Vec2(200 + 48 * 3 + 1, 200));
this->addChild(ball_red, 1);
auto charater = Sprite::createWithSpriteFrameName("charater.png");
charater->setContentSize(Size(48, 48));
charater->setPosition(Vec2(200 + 48 * 4 + 1, 200));
this->addChild(charater, 1);
return true;
}
初步绘制
初步绘制
// layer index
static constexpr int LAYER_INDEX_BACKGROUND = 1;
static constexpr int LAYER_INDEX_BALL = 2;
static constexpr int LAYER_INDEX_WALL = 3;
static constexpr int LAYER_INDEX_BOX = 4;
static constexpr int LAYER_INDEX_PLAYER = 5;
// 每一格宽度与高度
static constexpr int CELL_SIZE = 48;
// 墙壁图片
static const std::string WALL_PNG = "wall.png";
// 小球图片
static const std::string BALL_BLUE_PNG = "ball_blue.png";
static const std::string BALL_RED_PNG = "ball_red.png";
// 详细图片
static const std::string BOX_BLUE_PNG = "box_blue.png";
static const std::string BOX_RED_PNG = "box_red.png";
// 角色图片
static const std::string PLAYER_PNG = "charater.png";
// 墙壁位置信息
static std::vector<Vec2> wallPositions;
// 小球位置信息
static std::vector<Vec2> ballPositions;
// 箱子位置信息
static std::vector<Vec2> boxPositions;
// 角色位置信息
static Vec2 playerPosition;
// 初始化地图数据
static void initData();
/**
* @brief 绘制精灵
* @param scene 场景指针
* @param positions 精灵位置列表
* @param png 精灵图片名称
* @param layerIndex 所在层
*/
static void drawSprite(HelloWorld* scene, std::vector<Vec2> positions, std::string png, int layerIndex);
// 绘制墙壁
static void drawWall(HelloWorld* scene);
// 绘制小球
static void drawBall(HelloWorld* scene);
// 绘制箱子
static void drawBox(HelloWorld* scene);
// 绘制角色
static void drawPlayer(HelloWorld* scene);
bool HelloWorld::init()
{
// 先执行父类的 init
if (!Scene::init())
{
return false;
}
// 获得可视区域大小
const auto visibleSize = Director::getInstance()->getVisibleSize();
log("width:%f , height:%f", visibleSize.width, visibleSize.height);
// 获得原点位置 左下角
const Vec2 origin = Director::getInstance()->getVisibleOrigin();
log("x:%f , y:%f", origin.x, origin.y);
// 加载图集
auto spritecache = SpriteFrameCache::getInstance();
spritecache->addSpriteFramesWithFile("texture_packer/sprite.plist", "texture_packer/sprite.png");
// 设置背景
auto background = LayerColor::create(Color4B(25, 72, 148, 255));
this->addChild(background, LAYER_INDEX_BACKGROUND);
// 初始化游戏数据
initData();
// 绘制墙壁
drawWall(this);
// 绘制小球
drawBall(this);
// 绘制箱子
drawBox(this);
// 绘制角色
drawPlayer(this);
return true;
}
static void initData() {
// 初始化墙壁位置信息
wallPositions.push_back(Vec2(7, 12));
wallPositions.push_back(Vec2(8, 12));
wallPositions.push_back(Vec2(9, 12));
wallPositions.push_back(Vec2(7, 11));
wallPositions.push_back(Vec2(9, 11));
wallPositions.push_back(Vec2(7, 10));
wallPositions.push_back(Vec2(9, 10));
wallPositions.push_back(Vec2(10, 10));
wallPositions.push_back(Vec2(11, 10));
wallPositions.push_back(Vec2(12, 10));
wallPositions.push_back(Vec2(5, 9));
wallPositions.push_back(Vec2(6, 9));
wallPositions.push_back(Vec2(7, 9));
wallPositions.push_back(Vec2(12, 9));
wallPositions.push_back(Vec2(5, 8));
wallPositions.push_back(Vec2(10, 8));
wallPositions.push_back(Vec2(11, 8));
wallPositions.push_back(Vec2(12, 8));
wallPositions.push_back(Vec2(5, 7));
wallPositions.push_back(Vec2(6, 7));
wallPositions.push_back(Vec2(7, 7));
wallPositions.push_back(Vec2(8, 7));
wallPositions.push_back(Vec2(10, 7));
wallPositions.push_back(Vec2(8, 6));
wallPositions.push_back(Vec2(10, 6));
wallPositions.push_back(Vec2(8, 5));
wallPositions.push_back(Vec2(9, 5));
wallPositions.push_back(Vec2(10, 5));
// 初始化小球位置信息
ballPositions.push_back(Vec2(8, 11));
ballPositions.push_back(Vec2(11, 9));
ballPositions.push_back(Vec2(6, 8));
ballPositions.push_back(Vec2(9, 6));
// 初始化箱子位置信息
boxPositions.push_back(Vec2(8, 9));
boxPositions.push_back(Vec2(10, 9));
boxPositions.push_back(Vec2(8, 8));
boxPositions.push_back(Vec2(9, 7));
// 初始化角色位置信息
playerPosition.x = 9;
playerPosition.y = 8;
}
// 绘制 sprite
static void drawSprite(HelloWorld* scene, std::vector<Vec2> positions, std::string png, int layerIndex)
{
for (const Vec2 wallData : positions)
{
auto sprite = Sprite::createWithSpriteFrameName(png);
sprite->setContentSize(Size(CELL_SIZE, CELL_SIZE));
sprite->setPosition(Vec2(CELL_SIZE * wallData.x, CELL_SIZE * wallData.y));
scene->addChild(sprite, layerIndex);
}
}
// 绘制墙壁
static void drawWall(HelloWorld* scene)
{
drawSprite(scene, wallPositions, WALL_PNG, LAYER_INDEX_WALL);
}
// 绘制小球
static void drawBall(HelloWorld* scene)
{
drawSprite(scene, ballPositions, BALL_BLUE_PNG, LAYER_INDEX_BALL);
}
// 绘制箱子
static void drawBox(HelloWorld* scene)
{
drawSprite(scene, boxPositions, BOX_BLUE_PNG, LAYER_INDEX_BOX);
}
// 绘制角色
static void drawPlayer(HelloWorld* scene)
{
auto sprite = Sprite::createWithSpriteFrameName(PLAYER_PNG);
sprite->setContentSize(Size(CELL_SIZE, CELL_SIZE));
sprite->setPosition(Vec2(CELL_SIZE * playerPosition.x, CELL_SIZE * playerPosition.y));
scene->addChild(sprite, LAYER_INDEX_PLAYER);
}
效果如下
移动角色
- 重构了代码
- 角色基本移动
HelloWorld.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Scene
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
private:
// layer index
static constexpr int LAYER_INDEX_BACKGROUND = 1;
static constexpr int LAYER_INDEX_BALL = 2;
static constexpr int LAYER_INDEX_WALL = 3;
static constexpr int LAYER_INDEX_BOX = 4;
static constexpr int LAYER_INDEX_PLAYER = 5;
// 每一格宽度与高度
static constexpr int CELL_SIZE = 48;
// 墙壁图片
static const std::string WALL_PNG;
// 小球图片
static const std::string BALL_BLUE_PNG;
static const std::string BALL_RED_PNG;
// 详细图片
static const std::string BOX_BLUE_PNG;
static const std::string BOX_RED_PNG;
// 角色图片
static const std::string PLAYER_PNG;
private:
cocos2d::Sprite * player;
private:
// 墙壁位置信息
std::vector<Vec2> wallPositions;
// 小球位置信息
std::vector<Vec2> ballPositions;
// 箱子位置信息
std::vector<Vec2> boxPositions;
// 角色位置信息
Vec2 playerPosition;
// 初始化地图数据
void initData();
/**
* @brief 绘制精灵
* @param positions 精灵位置列表
* @param png 精灵图片名称
* @param layerIndex 所在层
*/
void drawSprite(std::vector<Vec2> positions, std::string png, int layerIndex);
// 绘制墙壁
void drawWall();
// 绘制小球
void drawBall();
// 绘制箱子
void drawBox();
// 绘制角色
void drawPlayer();
// 初始化键盘事件
void initKeyboardListener();
private:
/**
* @brief 角色移动
*
* @param dirVector 方向向量
* - Vec2(1,0):右
* - Vec2(-1,0):左
* - Vec2(0,1):上
* - Vec2(0,-1):下
*/
void playerMove(Vec2 dirVector);
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorld.cpp
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "CommonUtil.h"
#pragma warning(disable: 26429)
USING_NS_CC;
Scene* HelloWorld::createScene()
{
return HelloWorld::create();
}
// 墙壁图片
const std::string HelloWorld::WALL_PNG = "wall.png";
// 小球图片
const std::string HelloWorld::BALL_BLUE_PNG = "ball_blue.png";
const std::string HelloWorld::BALL_RED_PNG = "ball_red.png";
// 详细图片
const std::string HelloWorld::BOX_BLUE_PNG = "box_blue.png";
const std::string HelloWorld::BOX_RED_PNG = "box_red.png";
// 角色图片
const std::string HelloWorld::PLAYER_PNG = "charater.png";
bool HelloWorld::init()
{
// 先执行父类的 init
if (!Scene::init())
{
return false;
}
// 获得可视区域大小
const auto visibleSize = Director::getInstance()->getVisibleSize();
log("width:%f , height:%f", visibleSize.width, visibleSize.height);
// 获得原点位置 左下角
const Vec2 origin = Director::getInstance()->getVisibleOrigin();
log("x:%f , y:%f", origin.x, origin.y);
// 加载图集
auto spritecache = SpriteFrameCache::getInstance();
spritecache->addSpriteFramesWithFile("texture_packer/sprite.plist", "texture_packer/sprite.png");
// 设置背景
auto background = LayerColor::create(Color4B(25, 72, 148, 255));
this->addChild(background, LAYER_INDEX_BACKGROUND);
// 初始化游戏数据
initData();
// 绘制墙壁
drawWall();
// 绘制小球
drawBall();
// 绘制箱子
drawBox();
// 绘制角色
drawPlayer();
// 初始化键盘事件
initKeyboardListener();
return true;
}
void HelloWorld::initData() {
// 初始化墙壁位置信息
wallPositions.push_back(Vec2(7, 12));
wallPositions.push_back(Vec2(8, 12));
wallPositions.push_back(Vec2(9, 12));
wallPositions.push_back(Vec2(7, 11));
wallPositions.push_back(Vec2(9, 11));
wallPositions.push_back(Vec2(7, 10));
wallPositions.push_back(Vec2(9, 10));
wallPositions.push_back(Vec2(10, 10));
wallPositions.push_back(Vec2(11, 10));
wallPositions.push_back(Vec2(12, 10));
wallPositions.push_back(Vec2(5, 9));
wallPositions.push_back(Vec2(6, 9));
wallPositions.push_back(Vec2(7, 9));
wallPositions.push_back(Vec2(12, 9));
wallPositions.push_back(Vec2(5, 8));
wallPositions.push_back(Vec2(10, 8));
wallPositions.push_back(Vec2(11, 8));
wallPositions.push_back(Vec2(12, 8));
wallPositions.push_back(Vec2(5, 7));
wallPositions.push_back(Vec2(6, 7));
wallPositions.push_back(Vec2(7, 7));
wallPositions.push_back(Vec2(8, 7));
wallPositions.push_back(Vec2(10, 7));
wallPositions.push_back(Vec2(8, 6));
wallPositions.push_back(Vec2(10, 6));
wallPositions.push_back(Vec2(8, 5));
wallPositions.push_back(Vec2(9, 5));
wallPositions.push_back(Vec2(10, 5));
// 初始化小球位置信息
ballPositions.push_back(Vec2(8, 11));
ballPositions.push_back(Vec2(11, 9));
ballPositions.push_back(Vec2(6, 8));
ballPositions.push_back(Vec2(9, 6));
// 初始化箱子位置信息
boxPositions.push_back(Vec2(8, 9));
boxPositions.push_back(Vec2(10, 9));
boxPositions.push_back(Vec2(8, 8));
boxPositions.push_back(Vec2(9, 7));
// 初始化角色位置信息
playerPosition.x = 9;
playerPosition.y = 8;
}
// 绘制 sprite
void HelloWorld::drawSprite(std::vector<Vec2> positions, std::string png, int layerIndex)
{
for (const Vec2 wallData : positions)
{
auto sprite = Sprite::createWithSpriteFrameName(png);
sprite->setContentSize(Size(CELL_SIZE, CELL_SIZE));
sprite->setPosition(Vec2(CELL_SIZE * wallData.x, CELL_SIZE * wallData.y));
this->addChild(sprite, layerIndex);
}
}
// 绘制墙壁
void HelloWorld::drawWall()
{
drawSprite(wallPositions, HelloWorld::WALL_PNG, LAYER_INDEX_WALL);
}
// 绘制小球
void HelloWorld::drawBall()
{
drawSprite(ballPositions, BALL_BLUE_PNG, LAYER_INDEX_BALL);
}
// 绘制箱子
void HelloWorld::drawBox()
{
drawSprite(boxPositions, BOX_BLUE_PNG, LAYER_INDEX_BOX);
}
// 绘制角色
void HelloWorld::drawPlayer()
{
player = Sprite::createWithSpriteFrameName(PLAYER_PNG);
player->setContentSize(Size(CELL_SIZE, CELL_SIZE));
player->setPosition(Vec2(CELL_SIZE * playerPosition.x, CELL_SIZE * playerPosition.y));
this->addChild(player, LAYER_INDEX_PLAYER);
}
// 初始化键盘事件
void HelloWorld::initKeyboardListener()
{
auto listener = EventListenerKeyboard::create();
listener->onKeyReleased = [&](EventKeyboard::KeyCode keyCode, Event* e)
{
switch (keyCode)
{
case EventKeyboard::KeyCode::KEY_UP_ARROW: {
log("key released up");
playerMove(Vec2(0, 1));
break;
}
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW: {
log("key released right");
playerMove(Vec2(1, 0));
break;
}
case EventKeyboard::KeyCode::KEY_DOWN_ARROW: {
log("key released down");
playerMove(Vec2(0, -1));
break;
}
case EventKeyboard::KeyCode::KEY_LEFT_ARROW: {
log("key released left");
playerMove(Vec2(-1, 0));
break;
}
}
return true;
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, player);
}
void HelloWorld::playerMove(Vec2 dirVector)
{
// 更新角色位置信息
playerPosition.x += dirVector.x;
playerPosition.y += dirVector.y;
// 移动角色
const auto x = player->getPosition().x + CELL_SIZE * dirVector.x;
const auto y = player->getPosition().y + CELL_SIZE * dirVector.y;
player->setPosition(x,y);
}
本版本完成
HelloWorld
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
// 角色能否移动
typedef struct {
// 能否移动标志
bool flag;
// 箱子 tag
int box_tag;
} PLAYER_CAN_MOVE;
class HelloWorld : public cocos2d::Scene
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
private:
enum {
// 箱子 tag 起始值
BOX_TAG_START = 100
};
// layer index
static constexpr int LAYER_INDEX_BACKGROUND = 1;
static constexpr int LAYER_INDEX_BALL = 2;
static constexpr int LAYER_INDEX_WALL = 3;
static constexpr int LAYER_INDEX_BOX = 4;
static constexpr int LAYER_INDEX_PLAYER = 5;
// 每一格宽度与高度
static constexpr int CELL_SIZE = 48;
// 墙壁图片
static const std::string WALL_PNG;
// 小球图片
static const std::string BALL_BLUE_PNG;
static const std::string BALL_RED_PNG;
// 箱子图片
static const std::string BOX_BLUE_PNG;
static const std::string BOX_RED_PNG;
// 角色图片
static const std::string PLAYER_PNG;
private:
cocos2d::Sprite* player;
private:
// 墙壁位置信息
std::vector<Vec2> wallPositions;
// 小球位置信息
std::vector<Vec2> ballPositions;
// 箱子位置信息
std::vector<Vec2> boxPositions;
// 箱子 tag 和 位置信息 map
std::map<int, Vec2> boxTagPosMap;
// 角色位置信息
Vec2 playerPosition;
// 初始化地图数据
void initData();
/**
* @brief 绘制精灵
* @param positions 精灵位置列表
* @param png 精灵图片名称
* @param layerIndex 所在层
*/
void drawSprite(std::vector<Vec2> positions, std::string png, int layerIndex);
// 绘制墙壁
void drawWall();
// 绘制小球
void drawBall();
// 绘制箱子
void drawBox();
// 绘制角色
void drawPlayer();
// 初始化键盘事件
void initKeyboardListener();
private:
// 根据箱子坐标查找 tag
int findBoxTagByVec(Vec2 pos);
// 坐标是否在给定坐标列表中是
bool inPosition(std::vector<Vec2> positions, Vec2 pos);
// 角色能否移动
PLAYER_CAN_MOVE canPlayerMove(Vec2 dirVector);
/**
* @brief 角色移动
*
* @param dirVector 方向向量
* - Vec2(1,0):右
* - Vec2(-1,0):左
* - Vec2(0,1):上
* - Vec2(0,-1):下
*/
void playerMove(Vec2 dirVector);
// 刷新箱子颜色
void refreshBox();
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorld.cpp
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "CommonUtil.h"
#pragma warning(disable: 26429)
#pragma warning(disable: 26440)
USING_NS_CC;
Scene* HelloWorld::createScene()
{
return HelloWorld::create();
}
// 墙壁图片
const std::string HelloWorld::WALL_PNG = "wall.png";
// 小球图片
const std::string HelloWorld::BALL_BLUE_PNG = "ball_blue.png";
const std::string HelloWorld::BALL_RED_PNG = "ball_red.png";
// 详细图片
const std::string HelloWorld::BOX_BLUE_PNG = "box_blue.png";
const std::string HelloWorld::BOX_RED_PNG = "box_red.png";
// 角色图片
const std::string HelloWorld::PLAYER_PNG = "charater.png";
bool HelloWorld::init()
{
// 先执行父类的 init
if (!Scene::init())
{
return false;
}
// 获得可视区域大小
const auto visibleSize = Director::getInstance()->getVisibleSize();
log("width:%f , height:%f", visibleSize.width, visibleSize.height);
// 获得原点位置 左下角
const Vec2 origin = Director::getInstance()->getVisibleOrigin();
log("x:%f , y:%f", origin.x, origin.y);
// 加载图集
auto spritecache = SpriteFrameCache::getInstance();
spritecache->addSpriteFramesWithFile("texture_packer/sprite.plist", "texture_packer/sprite.png");
// 设置背景
auto background = LayerColor::create(Color4B(25, 72, 148, 255));
this->addChild(background, LAYER_INDEX_BACKGROUND);
// 初始化游戏数据
initData();
// 绘制墙壁
drawWall();
// 绘制小球
drawBall();
// 绘制箱子
drawBox();
// 绘制角色
drawPlayer();
// 初始化键盘事件
initKeyboardListener();
return true;
}
void HelloWorld::initData() {
// 初始化墙壁位置信息
wallPositions.push_back(Vec2(7, 12));
wallPositions.push_back(Vec2(8, 12));
wallPositions.push_back(Vec2(9, 12));
wallPositions.push_back(Vec2(7, 11));
wallPositions.push_back(Vec2(9, 11));
wallPositions.push_back(Vec2(7, 10));
wallPositions.push_back(Vec2(9, 10));
wallPositions.push_back(Vec2(10, 10));
wallPositions.push_back(Vec2(11, 10));
wallPositions.push_back(Vec2(12, 10));
wallPositions.push_back(Vec2(5, 9));
wallPositions.push_back(Vec2(6, 9));
wallPositions.push_back(Vec2(7, 9));
wallPositions.push_back(Vec2(12, 9));
wallPositions.push_back(Vec2(5, 8));
wallPositions.push_back(Vec2(10, 8));
wallPositions.push_back(Vec2(11, 8));
wallPositions.push_back(Vec2(12, 8));
wallPositions.push_back(Vec2(5, 7));
wallPositions.push_back(Vec2(6, 7));
wallPositions.push_back(Vec2(7, 7));
wallPositions.push_back(Vec2(8, 7));
wallPositions.push_back(Vec2(10, 7));
wallPositions.push_back(Vec2(8, 6));
wallPositions.push_back(Vec2(10, 6));
wallPositions.push_back(Vec2(8, 5));
wallPositions.push_back(Vec2(9, 5));
wallPositions.push_back(Vec2(10, 5));
// 初始化小球位置信息
ballPositions.push_back(Vec2(8, 11));
ballPositions.push_back(Vec2(11, 9));
ballPositions.push_back(Vec2(6, 8));
ballPositions.push_back(Vec2(9, 6));
// 初始化箱子位置信息
boxPositions.push_back(Vec2(8, 9));
boxPositions.push_back(Vec2(10, 9));
boxPositions.push_back(Vec2(8, 8));
boxPositions.push_back(Vec2(9, 7));
// 初始化角色位置信息
playerPosition.x = 9;
playerPosition.y = 8;
}
// 绘制 sprite
void HelloWorld::drawSprite(std::vector<Vec2> positions, std::string png, int layerIndex)
{
for (const Vec2 wallData : positions)
{
auto sprite = Sprite::createWithSpriteFrameName(png);
sprite->setContentSize(Size(CELL_SIZE, CELL_SIZE));
sprite->setPosition(Vec2(CELL_SIZE * wallData.x, CELL_SIZE * wallData.y));
this->addChild(sprite, layerIndex);
}
}
// 绘制墙壁
void HelloWorld::drawWall()
{
drawSprite(wallPositions, HelloWorld::WALL_PNG, LAYER_INDEX_WALL);
}
// 绘制小球
void HelloWorld::drawBall()
{
drawSprite(ballPositions, BALL_BLUE_PNG, LAYER_INDEX_BALL);
}
// 绘制箱子
void HelloWorld::drawBox()
{
for (int i = 0; i < boxPositions.size(); i++)
{
const auto& position = boxPositions.at(i);
auto sprite = Sprite::createWithSpriteFrameName(BOX_BLUE_PNG);
sprite->setContentSize(Size(CELL_SIZE, CELL_SIZE));
sprite->setPosition(Vec2(CELL_SIZE * position.x, CELL_SIZE * position.y));
sprite->setTag(i + 1);
this->addChild(sprite, LAYER_INDEX_BOX);
boxTagPosMap[i + 1] = position;
}
}
// 绘制角色
void HelloWorld::drawPlayer()
{
player = Sprite::createWithSpriteFrameName(PLAYER_PNG);
player->setContentSize(Size(CELL_SIZE, CELL_SIZE));
player->setPosition(Vec2(CELL_SIZE * playerPosition.x, CELL_SIZE * playerPosition.y));
this->addChild(player, LAYER_INDEX_PLAYER);
}
// 初始化键盘事件
void HelloWorld::initKeyboardListener()
{
auto listener = EventListenerKeyboard::create();
listener->onKeyReleased = [&](EventKeyboard::KeyCode keyCode, Event* e)
{
switch (keyCode)
{
case EventKeyboard::KeyCode::KEY_UP_ARROW: {
log("key released up");
playerMove(Vec2(0, 1));
break;
}
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW: {
log("key released right");
playerMove(Vec2(1, 0));
break;
}
case EventKeyboard::KeyCode::KEY_DOWN_ARROW: {
log("key released down");
playerMove(Vec2(0, -1));
break;
}
case EventKeyboard::KeyCode::KEY_LEFT_ARROW: {
log("key released left");
playerMove(Vec2(-1, 0));
break;
}
}
return true;
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, player);
}
int HelloWorld::findBoxTagByVec(Vec2 pos)
{
int tag = 0;
for (auto& it : boxTagPosMap)
{
if (pos.equals(it.second)) {
tag = it.first;
break;
}
}
return tag;
}
bool HelloWorld::inPosition(std::vector<Vec2> positions, Vec2 pos)
{
bool flag = false;
for (auto& v : positions)
{
if (pos.x == v.x && pos.y == v.y) {
flag = true;
break;
}
}
return flag;
}
PLAYER_CAN_MOVE HelloWorld::canPlayerMove(Vec2 dirVector)
{
PLAYER_CAN_MOVE canMove = { false,0 };
Vec2 nextPosition = Vec2(playerPosition.x + dirVector.x, playerPosition.y + dirVector.y);
// 判断移动方向上是否有箱子
const bool hasBox = inPosition(boxPositions, nextPosition);
// 如果有箱子, 则判断箱子下个位置是否还有箱子或者有边墙
if (hasBox)
{
// 下个位置是否有箱子或边墙
nextPosition.add(dirVector);
canMove.flag = !inPosition(boxPositions, nextPosition) && !inPosition(wallPositions, nextPosition);
}
else
{
// 没有箱子则判断墙壁
canMove.flag = !inPosition(wallPositions, nextPosition);
}
if (hasBox && canMove.flag)
{
nextPosition.subtract(dirVector);
canMove.box_tag = findBoxTagByVec(nextPosition);
}
return canMove;
}
void HelloWorld::playerMove(Vec2 dirVector)
{
const auto canMove = canPlayerMove(dirVector);
if (!canMove.flag) {
return;
}
// 更新箱子位置
if (0 != canMove.box_tag)
{
// 更新箱子位置信息
for (auto& it : boxTagPosMap)
{
if (canMove.box_tag == it.first) {
// it.second.add(dirVector);
boxPositions.at(it.first - 1).x += dirVector.x;
boxPositions.at(it.first - 1).y += dirVector.y;
it.second = boxPositions.at(it.first - 1);
break;
}
}
// 移动箱子
const auto box = getChildByTag(canMove.box_tag);
const auto boxx = box->getPosition().x + CELL_SIZE * dirVector.x;
const auto boxy = box->getPosition().y + CELL_SIZE * dirVector.y;
box->setPosition(boxx, boxy);
}
// 更新角色位置信息
playerPosition.x += dirVector.x;
playerPosition.y += dirVector.y;
// 移动角色
const auto x = player->getPosition().x + CELL_SIZE * dirVector.x;
const auto y = player->getPosition().y + CELL_SIZE * dirVector.y;
player->setPosition(x, y);
// 刷新箱子颜色
refreshBox();
}
void HelloWorld::refreshBox()
{
int size = 0;
for (auto& it : boxTagPosMap)
{
if (inPosition(ballPositions, it.second))
{
const auto box = dynamic_cast<Sprite*>(getChildByTag(it.first));
box->setDisplayFrame(SpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(BOX_RED_PNG));
box->setContentSize(Size(CELL_SIZE, CELL_SIZE));
size++;
}
}
if (size == boxPositions.size())
{
log("you win!");
}
}