问题描述
地毯元素,怎么实现类似液体一样的连接效果?
实现效果图

图片素材
备注:一个格子是140*140
左上角:footcloth_angle,其他几个角可以通过旋转得到。

底部:footcloth_bar_down

左边:footcloth_bar_left

右边:footcloth_bar_right

顶部:footcloth_bar_top

中心:footcloth_center

桥:footcloth_bridge

凹陷进去的角:footcloth_inner

代码
#ifndef __CARPET_NODE_H__
#define __CARPET_NODE_H__
#include "cocos2d.h"
struct CarpetBridge
{
CarpetBridge(cocos2d::Vec2 a0, cocos2d::Vec2 a1);
bool is_same(CarpetBridge st);
cocos2d::Vec2 p0;
cocos2d::Vec2 p1;
};
struct CarpetInner
{
CarpetInner(cocos2d::Vec2 a0, cocos2d::Vec2 a1, cocos2d::Vec2 a2);
bool is_same(CarpetInner st);
cocos2d::Vec2 p0;
cocos2d::Vec2 p1;
cocos2d::Vec2 p2;
};
class CarpetNode : public cocos2d::Node
{
public:
static CarpetNode * create(float af_cell_width, std::vector<std::vector<bool>> av_grid);
void updateWithGrid(std::vector<std::vector<bool>> av_grid);
protected:
bool init(float af_cell_width, std::vector<std::vector<bool>> av_grid);
void initGrid();
void drawGrid();
void drawCell(int x, int y);
void cleanCell(int x, int y);
void drawInner();
void drawBridge();
void drawCellLeftTop(int x, int y);
void drawCellRightTop(int x, int y);
void drawCellRightBottom(int x, int y);
void drawCellLeftBottom(int x, int y);
private:
bool isInGrid(int x, int y);
bool isSelectedCell(int x, int y);
void addInner(CarpetInner st);
void addBridge(CarpetBridge st);
private:
float m_cell_width;
std::vector<std::vector<bool>> m_data_arr;
std::vector<std::vector<cocos2d::Node*>> m_ui_arr;
std::vector<CarpetInner> m_inner_arr;
cocos2d::Node* m_inner_root;
std::vector<CarpetBridge> m_bridge_arr;
cocos2d::Node* m_bridge_root;
};
#endif
#include "CarpetNode.h"
CarpetBridge::CarpetBridge(cocos2d::Vec2 a0, cocos2d::Vec2 a1)
{
p0 = a0;
p1 = a1;
}
bool CarpetBridge::is_same(CarpetBridge st)
{
if (p0 == st.p0 && p1 == st.p1)
{
return true;
}
if (p1 == st.p0 && p0 == st.p1)
{
return true;
}
return false;
}
CarpetInner::CarpetInner(cocos2d::Vec2 a0, cocos2d::Vec2 a1, cocos2d::Vec2 a2)
{
p0 = a0;
p1 = a1;
p2 = a2;
}
bool CarpetInner::is_same(CarpetInner st)
{
std::vector<cocos2d::Vec2> v;
v.push_back(st.p0);
v.push_back(st.p1);
v.push_back(st.p2);
bool b_found_same = false;
for (auto it = v.begin(); it != v.end(); it++)
{
if (p0 == *it)
{
b_found_same = true;
v.erase(it);
break;
}
}
if (!b_found_same)
{
return false;
}
b_found_same = false;
for (auto it = v.begin(); it != v.end(); it++)
{
if (p1 == *it)
{
b_found_same = true;
v.erase(it);
break;
}
}
if (!b_found_same)
{
return false;
}
if (p2 != v[0])
{
return false;
}
return true;
}
CarpetNode * CarpetNode::create(float af_cell_width, std::vector<std::vector<bool>> av_grid)
{
auto p = new (std::nothrow) CarpetNode();
if (p && p->init(af_cell_width, av_grid))
{
p->autorelease();
return p;
}
delete p;
p = nullptr;
return nullptr;
}
bool CarpetNode::init(float af_cell_width, std::vector<std::vector<bool>> av_grid)
{
if (!cocos2d::Node::init())
{
return false;
}
m_cell_width = af_cell_width;
m_data_arr = av_grid;
initGrid();
m_inner_root = cocos2d::Node::create();
this->addChild(m_inner_root);
m_bridge_root = cocos2d::Node::create();
this->addChild(m_bridge_root);
drawGrid();
return true;
}
void CarpetNode::updateWithGrid(std::vector<std::vector<bool>> av_grid)
{
assert(m_data_arr.size() == av_grid.size());
for (int i = 0; i < av_grid.size(); i++)
{
assert(m_data_arr.at(i).size() == av_grid.at(i).size());
}
m_data_arr = av_grid;
drawGrid();
}
void CarpetNode::initGrid()
{
m_ui_arr.clear();
for (int x = 0; x < int(m_data_arr.size()); x++)
{
std::vector<cocos2d::Node*> vec;
for (int y = 0; y < int(m_data_arr.at(x).size()); y++)
{
auto lab = cocos2d::Label::create();
lab->setString(cocos2d::StringUtils::format("(%d,%d)", x, y));
lab->setPosition(cocos2d::Vec2(m_cell_width * (x + 0.5f), m_cell_width * (y + 0.5f)));
this->addChild(lab);
auto node = cocos2d::Node::create();
node->setPosition(cocos2d::Vec2(m_cell_width * (x + 0.5f), m_cell_width * (y + 0.5f)));
this->addChild(node);
vec.push_back(node);
}
m_ui_arr.push_back(vec);
}
}
void CarpetNode::drawGrid()
{
m_inner_arr.clear();
m_bridge_arr.clear();
for (int x = 0; x < int(m_data_arr.size()); x++)
{
for (int y = 0; y < int(m_data_arr.at(x).size()); y++)
{
cleanCell(x, y);
if (m_data_arr[x][y])
{
drawCell(x, y);
}
}
}
drawInner();
drawBridge();
}
void CarpetNode::drawCell(int x, int y)
{
drawCellLeftTop(x, y);
drawCellRightTop(x, y);
drawCellRightBottom(x, y);
drawCellLeftBottom(x, y);
}
void CarpetNode::cleanCell(int x, int y)
{
assert(m_ui_arr.at(x).at(y));
auto nod = m_ui_arr.at(x).at(y);
nod->removeAllChildren();
}
void CarpetNode::drawInner()
{
m_inner_root->removeAllChildren();
for (auto st : m_inner_arr)
{
float xmin = st.p0.x;
float ymin = st.p0.y;
xmin = std::min(xmin, st.p1.x);
ymin = std::min(ymin, st.p1.y);
xmin = std::min(xmin, st.p2.x);
ymin = std::min(ymin, st.p2.y);
float xmax = st.p0.x;
float ymax = st.p0.y;
xmax = std::max(xmax, st.p1.x);
ymax = std::max(ymax, st.p1.y);
xmax = std::max(xmax, st.p2.x);
ymax = std::max(ymax, st.p2.y);
assert(xmax - xmin == 1);
assert(ymax - ymin == 1);
float rotation = 0;
std::vector<cocos2d::Vec2> vec_temp;
vec_temp.push_back(st.p0);
vec_temp.push_back(st.p1);
vec_temp.push_back(st.p2);
cocos2d::Vec2 check_point(xmin, ymin);
if (vec_temp.end() == std::find(vec_temp.begin(), vec_temp.end(), check_point))
{
rotation = 270;
}
check_point = cocos2d::Vec2(xmin, ymax);
if (vec_temp.end() == std::find(vec_temp.begin(), vec_temp.end(), check_point))
{
rotation = 0;
}
check_point = cocos2d::Vec2(xmax, ymax);
if (vec_temp.end() == std::find(vec_temp.begin(), vec_temp.end(), check_point))
{
rotation = 90;
}
check_point = cocos2d::Vec2(xmax, ymin);
if (vec_temp.end() == std::find(vec_temp.begin(), vec_temp.end(), check_point))
{
rotation = 180;
}
auto spr_inner = cocos2d::Sprite::create("TestFootcloth/footcloth_inner.png");
spr_inner->setRotation(rotation);
spr_inner->setPosition(cocos2d::Vec2(m_cell_width * xmax, m_cell_width * ymax));
m_inner_root->addChild(spr_inner);
}
}
void CarpetNode::drawBridge()
{
m_bridge_root->removeAllChildren();
for (auto st : m_bridge_arr)
{
float rotation = 0;
auto p0 = st.p0;
auto p1 = st.p1;
if (p0.x + 1 == p1.x && p0.y + 1 == p1.y)
{
rotation = 90;
}
if (p0.x + 1 == p1.x && p0.y - 1 == p1.y)
{
rotation = 0;
}
if (p0.x - 1 == p1.x && p0.y - 1 == p1.y)
{
rotation = 90;
}
if (p0.x - 1 == p1.x && p0.y + 1 == p1.y)
{
rotation = 0;
}
int xmax = std::max(p0.x, p1.x);
int ymax = std::max(p0.y, p1.y);
auto spr_bridge = cocos2d::Sprite::create("TestFootcloth/footcloth_bridge.png");
spr_bridge->setRotation(rotation);
spr_bridge->setPosition(cocos2d::Vec2(m_cell_width * xmax, m_cell_width * ymax));
m_bridge_root->addChild(spr_bridge);
}
}
void CarpetNode::drawCellLeftTop(int x, int y)
{
assert(m_ui_arr.at(x).at(y));
auto nod = m_ui_arr.at(x).at(y);
bool a = false;
int xa = x - 1;
int ya = y;
a = isSelectedCell(xa, ya);
bool b = false;
int xb = x - 1;
int yb = y + 1;
b = isSelectedCell(xb, yb);
bool c = false;
int xc = x;
int yc = y + 1;
c = isSelectedCell(xc, yc);
if (false == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && false == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_left.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && false == c)
{
addBridge(CarpetBridge(cocos2d::Vec2(x, y), cocos2d::Vec2(x - 1, y + 1)));
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (true == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_top.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x - 1, y + 1), cocos2d::Vec2(x, y + 1), cocos2d::Vec2(x, y)));
}
if (true == a && false == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x, y + 1), cocos2d::Vec2(x, y), cocos2d::Vec2(x - 1, y)));
}
if (true == a && true == b && false == c)
{
addInner(CarpetInner(cocos2d::Vec2(x, y), cocos2d::Vec2(x - 1, y), cocos2d::Vec2(x - 1, y + 1)));
}
if (true == a && true == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_center.png");
spr->setPosition(cocos2d::Vec2(0 - m_cell_width / 4.0f, 0 + m_cell_width / 4.0f));
spr->setRotation(0);
nod->addChild(spr);
}
}
void CarpetNode::drawCellRightTop(int x, int y)
{
assert(m_ui_arr.at(x).at(y));
auto nod = m_ui_arr.at(x).at(y);
bool a = false;
int xa = x;
int ya = y + 1;
a = isSelectedCell(xa, ya);
bool b = false;
int xb = x + 1;
int yb = y + 1;
b = isSelectedCell(xb, yb);
bool c = false;
int xc = x + 1;
int yc = y;
c = isSelectedCell(xc, yc);
if (false == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(90);
nod->addChild(spr);
}
if (false == a && false == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_top.png");
spr->setAnchorPoint(cocos2d::Vec2(0, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && false == c)
{
addBridge(CarpetBridge(cocos2d::Vec2(x, y), cocos2d::Vec2(x + 1, y + 1)));
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(90);
nod->addChild(spr);
}
if (true == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_right.png");
spr->setAnchorPoint(cocos2d::Vec2(0, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x + 1, y + 1), cocos2d::Vec2(x + 1, y), cocos2d::Vec2(x, y)));
}
if (true == a && false == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x + 1, y), cocos2d::Vec2(x, y), cocos2d::Vec2(x, y + 1)));
}
if (true == a && true == b && false == c)
{
addInner(CarpetInner(cocos2d::Vec2(x, y), cocos2d::Vec2(x, y + 1), cocos2d::Vec2(x + 1, y + 1)));
}
if (true == a && true == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_center.png");
spr->setPosition(cocos2d::Vec2(0 + m_cell_width / 4.0f, 0 + m_cell_width / 4.0f));
spr->setRotation(0);
nod->addChild(spr);
}
}
void CarpetNode::drawCellRightBottom(int x, int y)
{
assert(m_ui_arr.at(x).at(y));
auto nod = m_ui_arr.at(x).at(y);
bool a = false;
int xa = x + 1;
int ya = y;
a = isSelectedCell(xa, ya);
bool b = false;
int xb = x + 1;
int yb = y - 1;
b = isSelectedCell(xb, yb);
bool c = false;
int xc = x;
int yc = y - 1;
c = isSelectedCell(xc, yc);
if (false == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(180);
nod->addChild(spr);
}
if (false == a && false == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_right.png");
spr->setAnchorPoint(cocos2d::Vec2(0, 1));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && false == c)
{
addBridge(CarpetBridge(cocos2d::Vec2(x, y), cocos2d::Vec2(x + 1, y - 1)));
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(180);
nod->addChild(spr);
}
if (true == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_down.png");
spr->setAnchorPoint(cocos2d::Vec2(0, 1));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x + 1, y - 1), cocos2d::Vec2(x, y - 1), cocos2d::Vec2(x, y)));
}
if (true == a && false == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x, y - 1), cocos2d::Vec2(x, y), cocos2d::Vec2(x + 1, y)));
}
if (true == a && true == b && false == c)
{
addInner(CarpetInner(cocos2d::Vec2(x, y), cocos2d::Vec2(x + 1, y), cocos2d::Vec2(x + 1, y - 1)));
}
if (true == a && true == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_center.png");
spr->setPosition(cocos2d::Vec2(0 + m_cell_width / 4.0f, 0 - m_cell_width / 4.0f));
spr->setRotation(0);
nod->addChild(spr);
}
}
void CarpetNode::drawCellLeftBottom(int x, int y)
{
assert(m_ui_arr.at(x).at(y));
auto nod = m_ui_arr.at(x).at(y);
bool a = false;
int xa = x;
int ya = y - 1;
a = isSelectedCell(xa, ya);
bool b = false;
int xb = x - 1;
int yb = y - 1;
b = isSelectedCell(xb, yb);
bool c = false;
int xc = x - 1;
int yc = y;
c = isSelectedCell(xc, yc);
if (false == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(270);
nod->addChild(spr);
}
if (false == a && false == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_down.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 1));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && false == c)
{
addBridge(CarpetBridge(cocos2d::Vec2(x, y), cocos2d::Vec2(x - 1, y - 1)));
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_angle.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 0));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(270);
nod->addChild(spr);
}
if (true == a && false == b && false == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_bar_left.png");
spr->setAnchorPoint(cocos2d::Vec2(1, 1));
spr->setPosition(cocos2d::Vec2(0, 0));
spr->setRotation(0);
nod->addChild(spr);
}
if (false == a && true == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x - 1, y - 1), cocos2d::Vec2(x - 1, y), cocos2d::Vec2(x, y)));
}
if (true == a && false == b && true == c)
{
addInner(CarpetInner(cocos2d::Vec2(x - 1, y), cocos2d::Vec2(x, y), cocos2d::Vec2(x, y - 1)));
}
if (true == a && true == b && false == c)
{
addInner(CarpetInner(cocos2d::Vec2(x, y), cocos2d::Vec2(x, y - 1), cocos2d::Vec2(x - 1, y - 1)));
}
if (true == a && true == b && true == c)
{
auto spr = cocos2d::Sprite::create("TestFootcloth/footcloth_center.png");
spr->setPosition(cocos2d::Vec2(0 - m_cell_width / 4.0f, 0 - m_cell_width / 4.0f));
spr->setRotation(0);
nod->addChild(spr);
}
}
bool CarpetNode::isInGrid(int x, int y)
{
if (x < 0 || y < 0)
return false;
if (x >= int(m_data_arr.size()))
return false;
if (y >= int(m_data_arr.at(x).size()))
return false;
return true;
}
bool CarpetNode::isSelectedCell(int x, int y)
{
if (!isInGrid(x, y))
return false;
return m_data_arr.at(x).at(y);
}
void CarpetNode::addInner(CarpetInner st)
{
bool flag = false;
for (auto st0 : m_inner_arr)
{
if (st0.is_same(st))
{
flag = true;
break;
}
}
if (!flag)
{
m_inner_arr.push_back(st);
}
}
void CarpetNode::addBridge(CarpetBridge st)
{
bool flag = false;
for (auto st0 : m_bridge_arr)
{
if (st0.is_same(st))
{
flag = true;
break;
}
}
if (!flag)
{
m_bridge_arr.push_back(st);
}
}
#pragma once
#ifndef __TEST_FOOTCLOTH_SCENE_H__
#include "cocos2d.h"
#include "CarpetNode.h"
class TestFootclothScene : public cocos2d::Scene
{
public:
static TestFootclothScene* create();
virtual bool init() override;
void initGridData();
void initBackground();
bool onTouchBegan(cocos2d::Touch * t, cocos2d::Event * e);
private:
int m_grid_num;
float m_cell_w;
std::vector<std::vector<bool>> m_d_arr;
std::vector<std::vector<cocos2d::Sprite*>> m_spr_arr;
CarpetNode* m_carpet_node;
};
#endif
#include "TestFootclothScene.h"
TestFootclothScene * TestFootclothScene::create()
{
auto p = new (std::nothrow) TestFootclothScene();
if (p && p->init())
{
p->autorelease();
return p;
}
delete p;
p = nullptr;
return nullptr;
}
bool TestFootclothScene::init()
{
if (!cocos2d::Scene::init())
{
return false;
}
m_grid_num = 5;
m_cell_w = 140;
initGridData();
initBackground();
m_carpet_node = CarpetNode::create(m_cell_w, m_d_arr);
this->addChild(m_carpet_node, 1);
auto listener = cocos2d::EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(TestFootclothScene::onTouchBegan, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
return true;
}
void TestFootclothScene::initGridData()
{
m_d_arr.clear();
for (int i = 0; i < m_grid_num; i++)
{
std::vector<bool> vec_row;
for (int j = 0; j < m_grid_num; j++)
{
vec_row.push_back(false);
}
m_d_arr.push_back(vec_row);
}
}
void TestFootclothScene::initBackground()
{
m_spr_arr.clear();
for (int x = 0; x < m_grid_num; x++)
{
std::vector<cocos2d::Sprite*> vec_row;
for (int y = 0; y < m_grid_num; y++)
{
std::string str1 = "TestFootcloth/map_cell_1.png";
if ((x % 2 == 1 && y % 2 == 0) || (x % 2 == 0 && y % 2 == 1))
{
str1 = "TestFootcloth/map_cell_2.png";
}
auto spr = cocos2d::Sprite::create(str1);
spr->setAnchorPoint(cocos2d::Vec2::ZERO);
spr->setPosition(cocos2d::Vec2(x * 140, y * 140));
this->addChild(spr);
vec_row.push_back(spr);
}
m_spr_arr.push_back(vec_row);
}
}
bool TestFootclothScene::onTouchBegan(cocos2d::Touch * t, cocos2d::Event * e)
{
auto world_pos = t->getLocation();
for (int i = 0; i < m_grid_num; i++)
{
for (int j = 0; j < m_grid_num; j++)
{
auto spr = m_spr_arr.at(i).at(j);
float sprw = spr->getContentSize().width;
float sprh = spr->getContentSize().height;
auto node_pos = spr->convertToNodeSpace(world_pos);
float posx = node_pos.x;
float posy = node_pos.y;
if (0 < posx && posx < sprw && 0 < posy && posy < sprh)
{
cocos2d::log("click(%d %d)", i, j);
m_d_arr.at(i).at(j) = !m_d_arr.at(i).at(j);
m_carpet_node->updateWithGrid(m_d_arr);
return false;
}
}
}
return false;
}