Here's the source code:
SmashPc 10-26-11 Code
First, a picture of the level from gleed2d:
As you can see, it's not much more than 4 walls. I added a block in the middle. a little circular wall with armor inside, some Enemy Generator Platforms (the things in the corner, will spawn enemies), and the little circles are the players spawn points.
As I said, this creates a xml file. So, I created the GameLevel.h and .cpp to parse that xml file and generate data we can understand.
The code is below, you can expand them to see it if you want. It's got a lot of xml parsing. I'll explain a little below.
GamLevel::GameLevel():
The code is below, you can expand them to see it if you want. It's got a lot of xml parsing. I'll explain a little below.
GamLevel::GameLevel():
GameLevel::GameLevel(char *pcFileToLoad) : mbValid(FALSE) { TiXmlDocument doc(pcFileToLoad); // load and check if it was successful if (doc.LoadFile()) { TiXmlElement *root = doc.RootElement(); // extract level name, can check at some point printf("Level name %s\n", root->Attribute("Name")); for(TiXmlElement* LayersElem = root->FirstChildElement(); LayersElem; LayersElem = LayersElem->NextSiblingElement()) { printf("LayersElem %s\n", LayersElem->ValueStr().c_str()); // this should be Layers if (LayersElem->ValueStr() == string("Layers")) { for(TiXmlElement* LayerElem = LayersElem->FirstChildElement(); LayerElem; LayerElem = LayerElem->NextSiblingElement()) { printf("LayerElem %s\n", LayerElem->ValueStr().c_str()); // This should be Layer if (LayerElem->ValueStr() == string("Layer")) { for(TiXmlElement* ItemsElem = LayerElem->FirstChildElement(); ItemsElem; ItemsElem = ItemsElem->NextSiblingElement()) { printf("ItemsElem %s\n", ItemsElem->ValueStr().c_str()); // This should be Items if (ItemsElem->ValueStr() == string("Items")) { for(TiXmlElement* ItemElem = ItemsElem->FirstChildElement(); ItemElem; ItemElem = ItemElem->NextSiblingElement()) { // This should be Item if (ItemElem->ValueStr() == string("Item")) { ParseItem(ItemElem); } } } else { // it's scoll speed, we can ignore printf("Skip Scroll\n"); } } } } } } mbValid = TRUE; } else { printf("Failed opening %s, reason: %s\n", pcFileToLoad, doc.ErrorDesc()); } }
GameLevel::ParseItem()
void GameLevel::ParseItem(TiXmlElement* ItemElem) { tLevelWall LevelWall; tLevelItem LevelItem; map SpecValues; // store off position cpVect Position; Position.x = atof(ItemElem->FirstChild("Position")->FirstChild("X")->FirstChild()->Value()); Position.y = atof(ItemElem->FirstChild("Position")->FirstChild("Y")->FirstChild()->Value()); // check for custom properties and handle if found if (ItemElem->FirstChildElement("CustomProperties")->FirstChild() != NULL) { string Key, Value; for(TiXmlElement* PropElem = ItemElem->FirstChildElement("CustomProperties")->FirstChildElement(); PropElem; PropElem = PropElem->NextSiblingElement()) { Key = PropElem->Attribute("Name"); Value = PropElem->FirstChild()->ValueStr(); printf("SpecField Key %s Val %s\n", Key.c_str(), Value.c_str()); SpecValues[Key] = Value; } } // check for spawn 1st if (strncmp(ItemElem->Attribute("Name"), "Spawn", 5) == 0) { mSpawns.push_back(Position); } /* If it's a rectangle, it's made as a wall or spawn point */ else if (strcmp(ItemElem->Attribute("xsi:type"), "RectangleItem") == 0) { LevelWall.StartingPoint = Position; LevelWall.EndingPoint.x = atof(ItemElem->FirstChild("Width")->FirstChild()->Value()) + Position.x; LevelWall.EndingPoint.y = atof(ItemElem->FirstChild("Height")->FirstChild()->Value()) + Position.y; LevelWall.bIsRect = TRUE; printf("Rect Start x %f end %f\n", LevelWall.StartingPoint.x, LevelWall.EndingPoint.x); printf("Rect Start y %f end %f\n", LevelWall.StartingPoint.y, LevelWall.EndingPoint.y); // add to vector mWalls.push_back(LevelWall); } else if (strcmp(ItemElem->Attribute("xsi:type"), "TextureItem") == 0) { string TempString; cpVect SecondLoc = cpvzero; LevelItem.Location = Position; printf("SpriteName %s\n", ItemElem->FirstChild("texture_filename")->FirstChild()->Value()); // get name up to _ TempString = string(ItemElem->Attribute("Name")); LevelItem.ItemName = TempString.substr(0, TempString.find("_")); cout << "LevelItem name " << LevelItem.ItemName << endl; // load the texture here LevelItem.pImage = Utilities::ImageGet(ItemElem->FirstChild("texture_filename")->FirstChild()->Value()); mItems.push_back(LevelItem); } else if (strcmp(ItemElem->Attribute("xsi:type"), "PathItem") == 0) { // Parse the Path starting at WorldPoints ParsePath(ItemElem->Attribute("Name"), ItemElem->FirstChild("WorldPoints")->FirstChild("Vector2")); } }
GameLevel::ParsePath()
void GameLevel::ParsePath(const char */*pName*/, const TiXmlNode *WorldCoordNode) { tLevelWall LevelWall; BOOL bFirst = TRUE; // Might use name if we use paths for more than walls in the future for(const TiXmlNode* CoordNode = WorldCoordNode; CoordNode; CoordNode = CoordNode->NextSibling()) { if (bFirst) { LevelWall.StartingPoint.x = atof(CoordNode->FirstChild("X")->FirstChild()->Value()); LevelWall.StartingPoint.y = atof(CoordNode->FirstChild("Y")->FirstChild()->Value()); bFirst = FALSE; } else { cpFloat SwapVal; LevelWall.EndingPoint.x = atof(CoordNode->FirstChild("X")->FirstChild()->Value()); LevelWall.EndingPoint.y = atof(CoordNode->FirstChild("Y")->FirstChild()->Value()); LevelWall.bIsRect = FALSE; mWalls.push_back(LevelWall); printf("Path Start x %f end %f\n", LevelWall.StartingPoint.x, LevelWall.EndingPoint.x); printf("Path Start y %f end %f\n", LevelWall.StartingPoint.y, LevelWall.EndingPoint.y); LevelWall.StartingPoint = LevelWall.EndingPoint; } } }
In order to use these, we modify SmashPcMap to load the Level, and it reads the lists out and draws the walls and items.
Here's the new CreateSpace, which puts the walls into the physics space:
void SmashPcMap::CreateSpace(void) { GameLevel::tLevelItem LevelItem; GameLevel::tLevelWall LevelWall; cpShape *pShape; std::vector::iterator it; /* loop through all the walls 1st and draw them */ for (it = mpLevel->mWalls.begin(); it != mpLevel->mWalls.end(); it++) { if (it->bIsRect) { cpVect Verts[4] = {{it->StartingPoint.x, it->StartingPoint.y}, {it->StartingPoint.x, it->EndingPoint.y}, {it->EndingPoint.x, it->EndingPoint.y}, {it->EndingPoint.x, it->StartingPoint.y} }; pShape = cpSpaceAddShape(mpSpace, cpPolyShapeNew(&mpSpace->staticBody, 4, Verts, cpvzero)); } else { pShape = cpSpaceAddShape(mpSpace, cpSegmentShapeNew(&mpSpace->staticBody, it->StartingPoint, it->EndingPoint, 10.0f)); } pShape->e = 0.0f;//9f; pShape->u = 0.0f;//3f; pShape->collision_type = WALL_COL_TYPE; pShape->data = this; // Walls occupy all layers } }
And here's the SmashPcMap::Draw that draws the walls and items:
BOOL SmashPcMap::Draw(cpBody *PlayerBody) { cpFloat LeftX = PlayerBody->p.x - mu32ScreenWidth/2; cpFloat TopY = PlayerBody->p.y - mu32ScreenHeight/2; std::vector::iterator it; /* loop through all the walls 1st and draw them */ for (it = mpLevel->mWalls.begin(); it != mpLevel->mWalls.end(); it++) { sf::Shape oShape; if (it->bIsRect) { oShape = sf::Shape::Rectangle (it->StartingPoint.x - LeftX, it->StartingPoint.y - TopY, it->EndingPoint.x - it->StartingPoint.x, it->EndingPoint.y - it->StartingPoint.y, sf::Color(128, 128, 128, 255)); } else { oShape = sf::Shape::Line(it->StartingPoint.x - LeftX, it->StartingPoint.y - TopY, it->EndingPoint.x - LeftX, it->EndingPoint.y - TopY, 10, sf::Color(128, 128, 128, 255)); } mpApp->Draw(oShape); } // draw all the items std::vector::iterator ItemIt; for (ItemIt = ItemList.begin(); ItemIt != ItemList.end(); ItemIt++) { (*ItemIt)->Draw(PlayerBody->p); } return TRUE; }
void SmashPcItem::CreatePhysicsObjects(void) { if (mbPhysical) { cpFloat Inertia; mpBody = cpBodyNew(500.0f, 500.0f); cpBodySetPos(mpBody, mItemInfo.Location); mpBody->v = cpvzero; /* teleporter doesn't spin */ mpBody->w = 2.0f; mpShape = cpCircleShapeNew(mpBody, (cpFloat)((mItemInfo.pImage->GetWidth() + mItemInfo.pImage->GetHeight())/4), cpvzero); Inertia = cpMomentForCircle(5.0f, 0.0f, (cpFloat)((mItemInfo.pImage->GetWidth() + mItemInfo.pImage->GetHeight())/4), cpvzero); cpBodySetMoment(mpBody, Inertia); mpShape->e = 0.0f; // not elatics, no bouncing mpShape->u = 1.0f; // 0.0 is frictionless mpShape->collision_type = ITEM_COL_TYPE; mpShape->layers = MAP_ITEM_LAYER; mpShape->data = static_cast<void *>(this); cpSpaceAddBody(mpSpace, mpBody); cpSpaceAddShape(mpSpace, mpShape); // Create Collision handler here, only player notifies the item // data can be NULL since we have class stored in Shape cpSpaceAddCollisionHandler(mpSpace, ITEM_COL_TYPE, PLAYER_COL_TYPE, SmashPcItem::PlayerCollision, NULL, NULL, NULL, NULL); } }
int SmashPcItem::PlayerCollision(cpArbiter *arb, struct cpSpace *space, void *data) { SmashPcItem *pItem; SmashPcPlayer *pPlayer; cpShape *pItemShape, *pPlayerShape; cpArbiterGetShapes(arb, &pItemShape, &pPlayerShape); pPlayer = reinterpret_cast(pPlayerShape->data); pItem = reinterpret_cast(pItemShape->data); // We have the player that hit, and the item he hit pItem->NotifyHit(pPlayer); }
Basically, it get the player and item objects that collided and notifies that item of being hit. Currently NotifyHit() doesn't do anything.
Next time, I'm going to do a little re-design. I notice the classes SmashPcPlayer, SmashPcItem, and SmashPcBullet all have physicsal properties and can eb Drawn, which I should inherit from one class to stop from using the same code over and over.
No comments:
Post a Comment