Sunsharior

Members
  • Content count

    43
  • Joined

  • Last visited

Community Reputation

1567 Excellent

About Sunsharior

  • Rank
    Member

Personal Information

  • Location
    Québec
  1. I would sacrifice a lot more things (i've already sacrificed so much), but i wouldn't sacrifice my happiness.
  2. The question is vague, but i'll try to answer anyway. Not too long ago, i've been at the same place where you are at the moment. The biggest problem i had was trying to assimilate everything at once.   Start with small bites.   1. First, design a simple Polygon class. Just try to make it work, and focus on that. 2. Once it's working, try to implement your own  SAT algorithm that work with your own polygon class. You'll need to understand how SAT work. 3. Once that is working, modify your sat algorythm to include the MTV.   Now i realize this might not help you, so here is my own SAT function you can read and try to understand (or even use if you want, but a mention would be nice, just don't claim it as your own). The code is well documented, should you have more question, i'll be happy to answer.   The Sat Function [spoiler] ////////////////////////////////////////////////////////////////////////// // Try to find if an Axis can disprove collision. bool CMath::Sat(CPolygon *_a, CPolygon *_b, Collision &_col) const { //A slope can be 6 pixel off and still be considered the best resolving vector. //this fix the polygon being wrongly accused of being a square when they are an exotic shape. const unsigned short slopebias = VERTICAL_TOUCH_DISTANCE; float overlap = 1000; // Large value. CVector smallestMTV(0,0); //MTV (Minimum translation vector). This is the resolving vector. //For polygon A... for (int i = 0; i < _a->n; i++) { // Get the direction vector CVector axis = _a->edges[i].dir; // Get the normal of the vector (90 degrees) axis = Perp(axis); bool slope = axis.x != 0 && axis.y != 0; // Find the projection of a and b onto axis CVector p1 = Project(_a,axis), p2 = Project(_b,axis); // If they do not overlap, then no collision float o = 1000; if (!Overlap(p1,p2, o)) return false; //Update the MTV else { //If the mtv is the smallest OR if the Axis is a slope, update it. //The slope part will fix the bug when the actor get stuck in slope's corners. if(o < overlap || (o < overlap + slopebias && slope) ) { if(o < overlap) overlap = o; smallestMTV = axis; } } } // repeat for b for (int i = 0; i < _b->n; i++) { CVector axis = _b->edges[i].dir; axis = Perp(axis); bool slope = axis.x != 0 && axis.y != 0; CVector p1 = Project(_a,axis), p2 = Project(_b,axis); float o = 1000; if (!Overlap(p1,p2, o)) return false; else { if(o == 0) return true; if(o < overlap || (o < overlap + slopebias && slope) ) { if(o < overlap) overlap = o; smallestMTV = axis; } } } //No Axis can prove the Polygon is not colliding, so it must be colliding. //Calculate the slope (if it's infinite, just put zero.) _col.slope = GetSlope(smallestMTV.x, smallestMTV.y); //normalize (to put things into pixel) smallestMTV = Normalize(smallestMTV); //Calculate the MTV; // If the slope is positive, take ceiling value, else take floor value. (FOR X ONLY) _col.mtv.x = _col.slope >= 0 ? -ceil(smallestMTV.x * overlap) : -floor(smallestMTV.x * overlap); //y is alway the ceil value. _col.mtv.y = -ceil(smallestMTV.y * overlap); return true; } ////////////////////////////////////////////////////////////////////////// // Perpendicular vector. CVector CMath::Perp(CVector _p) const {     CVector p(-_p.y, _p.x);     return p; } ////////////////////////////////////////////////////////////////////////// // bool CMath::Overlap(CVector _p1, CVector _p2, float &_overlap) const {     if (Contains(_p1,_p2, _overlap) )         return true;     return false; } ////////////////////////////////////////////////////////////////////////// // Projection bool CMath::Contains(CVector _range1, CVector _range2, float &_overlap) const {     _overlap = FastAbs(_range1.y - _range2.x);     return (_range1.y >= _range2.x && _range1.x <= _range2.y); } ////////////////////////////////////////////////////////////////////////// // CVector CMath::Project(CPolygon *_a, CVector _axis) const {     _axis = Normalize(_axis);     // min and max are the start and finish points     float min = Dot(_a->vertices[0], _axis);     float max = min;     for (int i = 0; i < _a->n; i++)     {         // find the projection of every point on the polygon onto the line.         float proj = Dot(_a->vertices[i], _axis);                  if (proj < min)             min = proj;         if (proj > max)             max = proj;     }     CVector arr (min, max); // To fix a warning.     return arr; } ////////////////////////////////////////////////////////////////////////// //Dot product float CMath::Dot(CVector _a, CVector _b) const {     return (_a.x * _b.x + _a.y * _b.y); } ////////////////////////////////////////////////////////////////////////// //Make the vector lenght = 1 CVector CMath::Normalize(CVector _p) const {     float l = SquareRoot(_p.x * _p.x + _p.y * _p.y);     // vector b is only of distance 1 from the origin     CVector b (_p.x / l, _p.y / l);     return b; } ////////////////////////////////////////////////////////////////////////// // float CMath::GetSlope(const CSegment &_s) const {     return GetSlope( (_s.second.y - _s.first.y), (_s.second.x - _s.first.x) ); } ////////////////////////////////////////////////////////////////////////// // Overload float CMath::GetSlope(const float &_risex, const float &_risey) const {     if(_risex != 0)         return _risey / _risex;     return 0; } [/spoiler]     The Collision structure, including the MTV.   [spoiler] //The result of a collision. //Mtv is the minimum translation vector. //slope is the slope of the touched segment. //Usefull for calculating if the actor slide off it if it's too steep. struct Collision { //default ctor Collision() : mtv(CVector(0,0) ), slope(0) {;} Collision(CVector _mtv, float _slope) : mtv(_mtv), slope(_slope) {;} CVector mtv; float slope; }; [/spoiler]   The Polygon struct. [spoiler] //A polygon is N Vector and N segment struct CPolygon { // Only pass clockwise set of vertices. CPolygon(std::vector _vectices) {Edit(_vectices);} //Edit the polygon void Edit(std::vector _vertices) { vertices.clear(); edges.clear(); n = _vertices.size(); if(n >= MIN_VALID_VERTICES) { for (size_t i = 0; i < _vertices.size() - 1; i++) { vertices.push_back(_vertices[i] ); edges.push_back(CSegment(_vertices[i], _vertices[i+1] ) ); } vertices.push_back(_vertices[n - 1] ); edges.push_back( CSegment(_vertices[n - 1], _vertices[0] ) ); } } int n; std::vector vertices; std::vector edges; }; [/spoiler]   The Segment struct   [spoiler] //A Segment is 2 vector and a direction. struct CSegment { CSegment() : first(CVector(0.0f, 0.0f)), second(CVector(0.0f, 0.0f)), dir( CVector(second.x - first.x, second.y - first.y) ) {;} CSegment(CVector _a, CVector _b) : first(_a), second(_b), dir( CVector(second.x - first.x, second.y - first.y) ) {;} void Clear() {first = CVector(0.0f, 0.0f); second = CVector(0.0f, 0.0f); dir = CVector(second.x - first.x, second.y - first.y);} CVector first, second, dir; }; [/spoiler]   The Vector struct [spoiler] //A vector is a X and a Y; struct CVector { CVector() : x(0), y(0) {;} CVector(float _x, float _y) : x(_x), y(_y){;} friend bool operator==(const CVector &_lhs, const CVector &_rhs){ return (_lhs.x == _rhs.x && _lhs.y == _rhs.y);} friend bool operator!=(const CVector &_lhs, const CVector &_rhs){ return !operator==(_lhs, _rhs);} CVector operator*(const float _rhs){ return CVector (x * _rhs, y * _rhs);} CVector operator+(const CVector &_rhs){ return CVector (x + _rhs.x, y + _rhs.y);} CVector operator-(const CVector &_rhs){ return CVector (x - _rhs.x, y - _rhs.y);} CVector& operator*=(const CVector &_rhs){x *= _rhs.x; y *= _rhs.y; return *this;} CVector& operator/=(const CVector &_rhs){x /= _rhs.x; y /= _rhs.y; return *this;} void Negate() {x = -x; y = -y;} double Dot(CVector _v2) { return x * _v2.x + y * _v2.y;} float x, y; }; [/spoiler]   Edit: I highly recommend this book. It explain everything you'll ever want to know about collisions.
  3. Also, each Menus can be a state inside the engine you are using, making a simple interface with Update, Draw and Clear or what ever fit your need. Consider using one state per menu, unless your design do not need it.   Just plug the current state inside the main game loop and change it when you need it.   We'll be happy to answers more question.
  4. I finally figured out the algorithm i needed. Here is my solution, in case some one need it (in C++).   Please note that my solution will scan along the X axis all the way to the end before going to the next row. This is exactly how my game need it, but i feel the need to mention it. The algorithm is not optimized. In my case, it did not need to be optimized. ////////////////////////////////////////////////////////////////////////// // Calculate the rectangles that can exist in a grid. std::vector<SDL_Rect> MapManager::CalculateVoidZones() {     std::vector<SDL_Point> corners;     std::vector<SDL_Point> sizes;     // Retrieve the maximum col and row. note that the col and row are already shrinked by 1.     SDL_Point max = {mInfo->GetCol(), mInfo->GetRow()};     // scan a col. Start from top left.     for(int row = 0; row <= max.y; row++)     {         // remember the current width for consecutive void state.         const short INVALID = -1;         SDL_Point lastcorner = {INVALID, INVALID}; // invalid corner.         int width = 0;         // scan a col. go right.         for(int col = 0; col <= max.x; col++)         {             bool lastcol = (col == mInfo->GetCol() && width > 0);             bool addnewcorner = false, nonvoid = true;             // if the cell is a void.             if(GetTile(row, col)->IsState(STATE_TILE_VOID))             {                 nonvoid = false;                 if(lastcorner.x == INVALID && lastcorner.y == INVALID)                 {                     lastcorner.x = col;                     lastcorner.y = row;                 }                                  bool addwidth = lastcorner.x == (col - width);                 if (addwidth)                     width++;                 if(!addwidth || lastcol)                     addnewcorner = true;             }             if(addnewcorner || (nonvoid && width))             {                 bool fit = false; // do the rectangle fit into a previous one?                 // loop through all existing corners                 for(unsigned int i = 0; i < corners.size(); i++)                 {                     if(corners[i].x == lastcorner.x &&                         corners[i].y == (lastcorner.y - sizes[i].y) &&                         width == sizes[i].x)                     {                         // increase the height                         sizes[i].y++;                         fit = true;                     }                 }                 if(!fit)                 {                     corners.push_back(lastcorner);                     SDL_Point size = SDL_Point();                     size.x = width;                     size.y = 1;                     sizes.push_back(size);                 }                 // reset                 lastcorner.x = INVALID;                 lastcorner.y = INVALID;                 width = 0;             }           }     }     // finally, create the rects     std::vector<SDL_Rect> voidzones;     if(corners.size() && corners.size() == sizes.size())     {         for(int i = corners.size(); i--;)         {             SDL_Point corner = corners[i];             SDL_Point size = sizes[i];                          // mTw and mTh is the width and height of a tile, respectively.             SDL_Rect rect = {corner.x * mTw,                              corner.y * mTh,                              size.x * mTw,                              size.y * mTh};             rects.push_back(rect);         }     } return voidzones;     } Have a good day.
  5. I've googled your answer, but what i found didn't seems related to my problem. I'll google more for the moment.
  6. Only the rectangles are interesting me.   This means a "star" shape composed of one 3x3 square and 1 filled cell on each side on the middle will be counted as 5 different rectangles. (the 3x3, and 4 1x1s).   0001000 0011100 0111110 0011100 0001000
  7. Hi. I'm currently trying to find a solution to a grid filling problem. Unfortunately, i'm having trouble imagining the implementation.   I will explain. In a 2D grid, a cell can be filled or not. I need to retrieve the total number of contiguous rectangles that can be formed by the filled regions. I'm fairly sure what i'm looking after have a name, but it's hard to google it without the proper name. I have attached an image of what i mean at the end of this post.   Currently, i have a double for loop for each rows and each columns. I remember the amount of "widths" of consecutive filled cell of the last row and if the current row have the same "widths" at the same place, i add one to that width height. But this is where i'm stuck, i can't find the rest of the logic. // This function will calculate the number of void zone and store it in mVoidZones void MapManager::CalculateVoidZones() { // array of rectangle (X, Y, W, H). mVoidZones.clear(); std::vector<SDL_Point> lastcorners; std::vector<int> widths; std::vector<int> heights; // scan a col. for(int col = 0; col < mInfo->GetCol(); col++) { // remember the current width a consecutive void state. int width = 0; SDL_Point lastcorner = SDL_Point(); lastcorner.x = -1; lastcorner.y = -1; // scan a row. for(int row = 0; row < mInfo->GetRow(); row++) { // fetch if the cell is "filled" if(GetTile(row, col)->IsState(STATE_TILE_VOID)) { // add a corner if(lastcorner.x == -1 && lastcorner.y == -1) { lastcorner.x = col; lastcorner.y = row; width = 1; } // increment the current width else if (lastcorner.x == col - 1) width++; } // a gap is found. Close the current width. else if(width > 0) { lastcorners.push_back(lastcorner); widths.push_back(width); lastcorner.x = -1; lastcorner.y = -1; width = 0; } } } // TODO this function is incomplete. // It is missing the "corner" comparison, then adding the height // then creating the rectangle and pushing it to mVoidZones. } Take note that the grid can be massively scrambled or even checkered, creating a huge number of small rectangles.   As always, thank you for your time. I will continue searching in the meantime.
  8. I'll add my advices as well. Code writting : Write small and focused function. The smaller and simpler, the better. Write Self documenting code. Your code must be so simple, just reading throught it explain what it does. The fonction name and it's parameters should explain near 100% of what it'll do. Exemple : If i look at "void Math::SortArray(std::array<T> &_array, bool _ascending) const;" i know that it return nothing, that the parameter _array will be modified by the function, that i can sort ascending or not with the second parameter and that the function will not motify the class "Math", as it is const. Comment anything that could be misinterpreted or cannot be made simple. If you have a programming style, stick to it. Never deviate. Everyone in your team must be up to date with the programming style and must follow it. Never use magic numbers. Use enumerators, or const in c/c++ and final in java. Never duplicate large chunk of code. Example: if you have a function that sort integer and another that sort double, rewrite it as a template. If you are programming with objects, encapsulate everything that can be encapsulated. Every class function must be private, unless it need to be public. Learn the most usefull programming patterns*. *Try to avoid the singleton programming patterns. Sometime, take the time to return to something that you finished and think about if it could be written simpler. Do a little optimization and refactoring here and there during developpement. Don't forget to retest what you change. if you are using a language that use header, try as hard as you can to maintain a clean and well sorted header (with comment). Group all draws function, group all getter, all setters, etc... This fall into your programming style or choice. Remove anything that become obsolete or unused. Don't worry about losing anything, you have a source control. The worst thing you could do is commenting whole functions. Its making scrolling through the code unnecessary obfuscated. Don't over-complicate anything. A lot of thing can be made simple just by taking the time to think it through. If you are writing something new, make sure it don't already exist first. Nobody wants three functionally identical functions that return the bounding box or an actor, written by 3 different guys / gals. This wikipedia page list a lot of things that can overcomplicate code and make it harder to manage. Print it and pin it on the dashboard where you work.   Code documentation :   Use a version control, git, mercurial, etc... and a version control software like svn or SourceTree. Always commit something with zero errors, zero warnings. Try to maintain the documentation up to date with the code. Atlassian have a wide variety of tools for a small team, without paying too much (Confluence for documentation, Jira for bugs, Hipchat for chat, Bamboo for building new versions. Bitbucket for source control). I highly recommend it.
  9. The observer design pattern is a good idea for decoupled achievements. Here is a link to an article describing it: http://gameprogrammingpatterns.com/observer.html
  10. You are missing the methods "clean" and "update" from the file SDLGameObject.cpp
  11. If you prefer to do thing yourself, i have something for you that you might like. I'll just explain how i did something similar in my game. With more than 250 weapons sprites, it was out of the question to actually draw each sprite. So what i did was creating my own tool to manage the sprite animations and hitboxes. After that, i added "anchors" to each frame of animation to set where the weapon goes. Good thing is, i can even set anchor point for the weapon when it's sheathed. You need two anchors, one to set where it goes and another to set the angle. So basically, i'm not using a complexe bone system but only anchor points to tell my game "that weapon goes there" at that frame. Obviously, this method was very time consuming, but it give you an alternative to Spine.
  12. With this simple snipet, you will be able to draw a sprite. // for this to work, i will assume you already have a SDL_Renderer and a SDL_Window. SDL_Texture *texture = IMG_LoadTexture(renderer, "image.png"); // if the texture was loaded. if(texture) { SDL_Rect rect = {0, 0, 32, 32}; // the rect is where you wants the texture to be drawn (screen coordinate). SDL_Rect crop = {0, 0, 16, 16}; // the crop is what part of the image we want to display. float angle = 180.0f; // set the angle. SDL_Point center = {8, 8}; // the center where the texture will be rotated. SDL_RendererFlip flip = SDL_FLIP_NONE; // the flip of the texture. // now, try to draw something int result = SDL_RenderCopyEx(renderer, texture, &crop , &rect, angle, &center, flip); if(result!= 0) std::cout << SDL_GetError() << std::endl; } else std::cout << Error << "Couldn't render texture." << std::endl; Using this example, you will draw an image rotated 180 degree. It will be drawn at 0,0 with a width of 32 and a height of 32. The crop is 16x16 and the rotation is 180 that is centered at 8,8.   I hope this help. If you have errors, feel free to post it.
  13. Like Josh Petrie said, you declared functions in a header file that were never defined elsewhere. I have enclosed 3 of your project's file code with the error fixed.   You will noticed a few things:   1. I have created the Prologue.cpp file and moved the content from Char info.cpp to it. 2. The prologue is now used as a class instead of static. See the modification i did to Main.cpp to notice it's scope remain inaffected. 3. The fonctions and member variables of the class Prologue are no longer static. Prologue.cpp now have the proper definition of thoses fonctions, and a constructor aswell. 4. The file Char info.cpp has been deleted.
  14. Thank you for your reply Dejaime. It helped me do the initial setup.   After fiddling this weekend, and doing additional researches, i've partially solved my problem. I'm not done yet but i've successfully draw something in QT using Sdl2. Now, what i'm missing is just the Inter-process communication between the QT application and my game. I should be able to do that during this week.   So, it's looking good, at the moment :).
  15. Hello everyone.   The video game engine i'm working on is amost complete. Right now i'm working on refactoring part i don't like. One of the part i do not like is the current level editor. It's currently a seperate window that have the basic fonctionality (see screenshot). So i wants to make everything nicer and do a level editor using QT.   I want to make a basic QT Dialog that have embedded inside the game engine. However, i'm very confused about how i should do it. Right now, i'm struggling to make SDL2 work within QT and i'm not even sure if it's the right approche i'm doing. I'm currently able to draw with SDL2 within QT, but i really don't know how to make my game work inside.   Some detail: i'm using QT 5.3.x via Vs2012 (not qteditor). My game engine is an executable that use SDL2 for graphic, sound and key inputs. I've heard i might have problem with graphic rendering and sound, so i'm hesitant on how to proceed. Should i make my game engine a DLL to run within QT or is a .Exe okay? If more information is needed, i will give it.   As always, thank you very much for your time. ps. Sorry for my english.