#include "olcUTIL_Geometry2D.h" #include "TSXParser.h" #include "olcPGEX_TransformedView.h" #include "olcUTIL_Camera2D.h" #include "olcPGEX_QuickGUI.h" using namespace olc; using namespace olc::utils; using namespace QuickGUI; const std::string TILESET_DIR="./Tiles/"; class TiledCollisionEditor : public olc::PixelGameEngine { std::unordered_maptilesets; std::unordered_mapimages; std::string activeTileset; Quadrilateral*editingQuad=nullptr; Quadrilateral originalQuad; std::string selectedObj=""; int editingPoint=4; //0-3 for the index we are editing within editingQuad. bool dragging=false; bool dragTranslate=false; vf2d upperLeftDragOffset{}; bool dragNewObj=false; vi2d upperLeftObjTile{}; Renderable circle; Renderable createNewButtonImg; Renderable editButtonImg; TransformedView view; public: TiledCollisionEditor() { sAppName = "TiledCollisionEditor"; } Manager gui; ImageCheckBox*createNewButton; ImageCheckBox*editButton; public: bool OnUserCreate() override { SetFontSprite("font3.png"); circle.Create(5,5); SetDrawTarget(circle.Sprite()); Clear(BLANK); FillCircle({2,2},2); SetDrawTarget(nullptr); circle.Decal()->Update(); createNewButtonImg.Load("newCollisionButton.png"); editButtonImg.Load("EditButton.png"); std::string tilesetFilename=TILESET_DIR+"Basic Tileset.tsx"; Tileset&tileset=tilesets[tilesetFilename]=TSXParser{tilesetFilename}.GetData(); Renderable&tilesetImg=images[tilesetFilename]; tilesetImg.Load(TILESET_DIR+tileset.filename); if(tilesets.size()==1)activeTileset=tilesetFilename; createNewButton=new ImageCheckBox{gui,createNewButtonImg,false,vf2d{4.f,ScreenHeight()-36.f},{32.f,32.f},{4,1},{32,32}}; createNewButton->hotkey=Q; editButton=new ImageCheckBox{gui,editButtonImg,true,vf2d{40.f,ScreenHeight()-36.f},{32.f,32.f},{4,4},{32,32}}; editButton->hotkey=E; return true; } float hundredsDigit=0.f; float tensDigit=0.f; float onesDigit=0.f; float currentHealth=0; float targetHealth=60; void Update(){ const Tileset&tileset=tilesets[activeTileset]; if(selectedObj.length()>0){ for(int y=0;ybChecked){ if(GetMouse(Mouse::LEFT).bPressed){ #pragma region Select a point on a collision quad. for(const Quadrilateral&quad:obj.collisionTiles){ for(size_t pointInd=0;const vf2d&point:quad){ if(geom2d::line(point,view.ScreenToWorld(GetMousePos())).length()<4){ if(GetMouse(Mouse::LEFT).bPressed){ editingPoint=pointInd; editingQuad=const_cast(&quad); originalQuad=quad; } goto exitCollisionCheck; } pointInd++; } } #pragma endregion for(const Quadrilateral&quad:obj.collisionTiles){ std::array,2>collisionTris{ geom2d::triangle{quad[0],quad[1],quad[2]}, geom2d::triangle{quad[0],quad[2],quad[3]}, }; for(geom2d::triangle&tri:collisionTris){ if(geom2d::contains(tri,view.ScreenToWorld(GetMousePos()))){ dragTranslate=true; editingQuad=const_cast(&quad); upperLeftDragOffset=GetSnapPoint()-quad[0]; originalQuad=*editingQuad; goto exitCollisionCheck; } } } exitCollisionCheck: if(EditingQuad&&!dragging){ (*editingQuad)[editingPoint]=GetSnapPoint(); editingPoint++; } } if(GetMouse(Mouse::RIGHT).bPressed||GetKey(ESCAPE).bPressed){ if(EditingQuad||dragTranslate){ editingPoint=4; dragging=false; *editingQuad=originalQuad; editingQuad=nullptr; dragTranslate=false; } } }else{ if(GetMouse(Mouse::LEFT).bPressed){ Quadrilateral newQuad{GetSnapPoint()}; tilesets[activeTileset].objects[selectedObj].collisionTiles.push_back(newQuad); dragging=true; editingQuad=const_cast(&obj.collisionTiles.back()); originalQuad=*editingQuad; } } if(GetMouse(Mouse::RIGHT).bPressed&&!EditingQuad&&!dragTranslate){ std::vectorquadsToBeRemoved; for(const Quadrilateral&quad:obj.collisionTiles){ std::array,2>collisionTris{ geom2d::triangle{quad[0],quad[1],quad[2]}, geom2d::triangle{quad[0],quad[2],quad[3]}, }; for(geom2d::triangle&tri:collisionTris){ if(geom2d::contains(tri,view.ScreenToWorld(GetMousePos()))){ //Delete this quad, the mouse is overlapping it! quadsToBeRemoved.push_back(const_cast(&quad)); break; } } } for(Quadrilateral*quad:quadsToBeRemoved){ std::erase_if(tilesets[activeTileset].objects[selectedObj].collisionTiles,[&](Quadrilateral&q){return &q==quad;}); } } if(GetMouse(Mouse::LEFT).bReleased){ if(EditingQuad&&dragging){ vf2d cursorPos=GetSnapPoint(); vf2d initialPoint=(*editingQuad)[0]; (*editingQuad)[1]=vf2d{cursorPos.x,initialPoint.y}; (*editingQuad)[2]=GetSnapPoint(); (*editingQuad)[3]=vf2d{initialPoint.x,cursorPos.y}; dragging=false; }else if(EditingQuad&&!dragging){ (*editingQuad)[editingPoint]=GetSnapPoint(); editingPoint=4; } dragTranslate=false; } } } void NewObjectUpdate(){ if(GetMouse(Mouse::LEFT).bPressed){ const Tileset&tileset=tilesets[activeTileset]; vf2d worldCoords=view.ScreenToWorld(GetMousePos()); upperLeftObjTile=vf2d{round(worldCoords.x/tileset.tilewidth)*tileset.tilewidth,round(worldCoords.y/tileset.tileheight)*tileset.tileheight}; dragNewObj=true; } if(GetMouse(Mouse::LEFT).bReleased){ dragNewObj=false; const Tileset&tileset=tilesets[activeTileset]; //Check for intersection with other objects, if found then we deny creating this object this way. for(auto&[name,obj]:tileset.objects){ } } } bool OnUserUpdate(float fElapsedTime) override { Clear(VERY_DARK_BLUE); view.HandlePanAndZoom(); const float CAMERA_MOVESPD = 150.f; if(GetKey(W).bHeld)view.MoveWorldOffset(vf2d{0.f,-CAMERA_MOVESPD}*fElapsedTime/view.GetWorldScale()); if(GetKey(S).bHeld)view.MoveWorldOffset(vf2d{0.f,CAMERA_MOVESPD}*fElapsedTime/view.GetWorldScale()); if(GetKey(A).bHeld)view.MoveWorldOffset(vf2d{-CAMERA_MOVESPD,0.f}*fElapsedTime/view.GetWorldScale()); if(GetKey(D).bHeld)view.MoveWorldOffset(vf2d{CAMERA_MOVESPD,0.f}*fElapsedTime/view.GetWorldScale()); const Tileset&tileset=tilesets[activeTileset]; view.DrawDecal({0,0},images[activeTileset].Decal()); for(int y=0;ybPressed)editButton->bChecked=false; if(editButton->bPressed)createNewButton->bChecked=false; if(GetMouseY()72){ if(!dragNewObj){ Update(); }else{ NewObjectUpdate(); } } //Font test. /*DrawStringDecal({0,0},"the quick brown fox jumps over the lazy dog 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f}); DrawStringDecal({0,18},"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f}); DrawStringPropDecal({0,36},"the quick brown fox jumps over the lazy dog 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f}); DrawStringPropDecal({0,54},"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f}); */ if(selectedObj.length()>0&&!dragNewObj){ const TilesetObject&obj=tileset.objects.at(selectedObj); Quadrilateral*highlightedQuad=nullptr; for(const Quadrilateral&quad:obj.collisionTiles){ std::array,2>collisionTris{ geom2d::triangle{quad[0],quad[1],quad[2]}, geom2d::triangle{quad[0],quad[2],quad[3]}, }; for(geom2d::triangle&tri:collisionTris){ if(geom2d::contains(tri,view.ScreenToWorld(GetMousePos()))){ highlightedQuad=const_cast(&quad); goto renderQuads; } } } renderQuads: for(const Quadrilateral&quad:obj.collisionTiles){ std::vectorpoints; std::vectoruvs; std::vectorcols; points.assign(quad.begin(),quad.end()); uvs.assign(4,{0.f,0.f}); cols.assign(4,(!GetMouse(Mouse::LEFT).bHeld&&highlightedQuad==&quad)?Pixel{255,20,20,150}:Pixel{255,40,40,128}); view.DrawPolygonDecal(nullptr,points,uvs,cols); for(bool highlighted=false;const vf2d&point:quad){ if(geom2d::line(point,view.ScreenToWorld(GetMousePos())).length()<4&&!highlighted){ view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,{1.f,1.f},YELLOW); highlighted=true; }else{ view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,{1.f,1.f},RED); } } } }else if(dragNewObj){ } for(auto&[objName,obj]:tileset.objects){ view.DrawLineDecal(obj.bounds.pos,obj.bounds.pos+vf2d{0.f,float(obj.bounds.size.y)},YELLOW); view.DrawLineDecal(obj.bounds.pos,obj.bounds.pos+vf2d{float(obj.bounds.size.x),0.f},YELLOW); view.DrawLineDecal(obj.bounds.pos+obj.bounds.size,obj.bounds.pos+obj.bounds.size+vf2d{0.f,-float(obj.bounds.size.y)},YELLOW); view.DrawLineDecal(obj.bounds.pos+obj.bounds.size,obj.bounds.pos+obj.bounds.size+vf2d{-float(obj.bounds.size.x),0.f},YELLOW); vi2d nameTextSize=GetTextSizeProp(objName)*0.25f; view.GradientFillRectDecal(obj.bounds.pos,nameTextSize+vf2d{2,2},RED,{255,0,0,64},{255,0,0,64},RED); view.DrawStringPropDecal(obj.bounds.pos+vf2d{1.25f,1.25f},objName,BLACK,vf2d{0.25f,0.25f}); view.DrawStringPropDecal(obj.bounds.pos+vf2d{1,1},objName,WHITE,vf2d{0.25f,0.25f}); if(geom2d::contains(obj.bounds,view.ScreenToWorld(GetMousePos()))){ selectedObj=objName; } } gui.Update(this); gui.DrawDecal(this); DrawStringDecal(createNewButton->vPos+vf2d{3,0},"Q"); DrawStringDecal(editButton->vPos+vf2d{3,0},"E"); return true; } }; int main() { TiledCollisionEditor demo; if (demo.Construct(640, 180, 4, 4)) demo.Start(); return 0; }