Jump to content
  • Advertisement

molehill mountaineer

  • Content Count

  • Joined

  • Last visited

Community Reputation

595 Good

About molehill mountaineer

  • Rank
  1. molehill mountaineer

    Is the Game Industry a Bad Place to Work?

  2. molehill mountaineer

    Arkanong part 6: batching draw calls

    Once again I must apologize for how long it took to post this - There are many things competing for my time so this journal doesn't always get the tender love and care it should. I've managed to find a few spare hours so I'm going to show you how I batched the draw calls for my game objects. Up to this point every game object has been in charge of drawing itself - that is, each object has its own texture and draw() function which loads and then renders the sprite. The object manager loops over all the objects and calls Object->draw() for every one of them. This approach is fine if your game doesn't have that many objects but ideally you should try to batch your draw calls because they are computationally expensive. Batching draw calls means that you only call draw() once for every texture rather than once for every object. So if you have 50 objects which use 2 textures (e.g. 25 blue balls and 25 red balls) you will only have to call draw() twice instead of 50. Since we're only calling draw() once for every texture, we need a way to tell the renderer where the texture is to be applied (that is, what objects will be using this texture). Luckily SFML has a class which does exactly this: the vertex array A vertex is a (graphical) point which has the following datamembers: - a position (x and y coordinates) - a color (I won't be using this since I'm drawing textures) - a pair of texture coordinates (which determine the part of the texture you want to use) Every game object consist of a number of these vertices - a triangle would have 3, a rectangle 4 (which is why it is commonly referred to as a 'quad'). So really they are just the "corners" of your object with a bit of extra information attached. The texture coordinates in particular are handy because you can use them to make draw calls more efficient. Let's say that I put the "red ball" and "blue ball" textures in a single texture called "balls". By specifying the texture coordinates so that only the left or right half of the texture is drawn I can "cut out" a piece of the image and apply it to my game object. If I did this I would be able to render all 50 blue/red balls with only one draw call since they are drawing off the same texture. You can see an example of this technique in the link I provided above. Even when you specify texture coordinates the renderer will not automatically know how to display the vertices. 4 points could be a 'filled in' rectangle or 4 lines or really just 4 single points - you might even draw the objects with a texture one minute and press a button later to display everything as lines to produce a 'wireframe' effect. So in order to draw a texture using vertex arrays we will need to provide the following to the renderer: - The texture (duh) - The collection of points that use this texture (i.e. all the corners that make up the objects that use this texture) - The way that they should be drawn (Quad, line, point respectively meaning 'filled in', 'wireframe' and 'single points'. There are other so-called primitive types but for now just knowing about quads will suffice) The textures are kept in a map inside a new managing class called TextureManager. If you're not familiar with the map datatype, it's a way to store key/value pairs similar to the way primary keys work for a database. The texture is associated with a unique string value (in this case, the name of the texture). If you'd like to know more check out the wikipedia articles on associative arrays and associative containers. The game objects store their own vertices and a string that corresponds to one of the key values from the texture map. For example: the texturemap might contain a texture with key-string "blue ball". Every game object that uses this texture would have the datamember textureName set to "blue ball". When the renderer want to draw the blue ball texture it loops over all the objects and collects the vertices that belong to objects with textureName set to "blue ball". In pseudocode, the steps taken to render all the textures look like this: - Loop over the different textures - Create a vertex array to store the points of all the objects that use this texture - Loop over all the game objects - If object::textureName equals texture key-string: collect the vertices for this object and add them to the vertex array - end of game object loop - call draw() with the current texture and the vertex array- end of texture loop Here's what that looks like in code. Though the syntax might be a little confusing it's really just following the steps I outlined above. void TextureManager::drawAllTextures(std::vector& objectList){ std::map::const_iterator textureIterator = m_textures.begin(); //loop over all textures while (textureIterator != m_textures.end()) { //vertex array for the current texture sf::VertexArray* vertexArray = new sf::VertexArray(sf::Quads); //collect all vertices which use this texture std::vector::const_iterator objectIterator = objectList.begin(); while (objectIterator != objectList.end()) { //if the object uses this texture //retrieve object's tranformed vertices into vertexArray if ((*objectIterator)->getTextureName() == textureIterator->first) { (*objectIterator)->getTransformedVertices(vertexArray); } ++objectIterator; } //draw the current texture and move on drawTexture(textureIterator->first, vertexArray); ++textureIterator; }} The method 'getTransformedVertices()' retrieves the (transformed) points that make up the object in question. If you don't understand what the word 'tranformed' means it's a way of 'positioning' the object in a certain place. The object knows its own points in a local frame of reference (my toes are 1.7 meters below my head and 0 meters in front of my nose) - transforming these points places them in the 'global' space of the game (my toes are 10 meters away from the nearest crosswalk and 100 meters below the top of that building). It's outside of the scope of this article to explain this properly so if you don't understand what I'm talking about I strongly recommend that you learn more about the use of matrixes in game development. You could start here. The benefit of working this way is that I don't have to recalculate all the points every time an object moves. I simply have to adjust the transformation matrix which 'places' the vertices in the game world. The same goes for scaling and rotation so it's a pretty neat trick to have in your toolbox. Once all the points for the current texture have been collected, the texture is rendered in the following piece of code:void TextureManager::drawTexture(std::string name, sf::VertexArray* vertices){ sf::Texture* texture = m_textures.at(name); if (texture != 0) { sf::RenderStates renderstate; renderstate.texture = texture; m_pRenderWindow->draw(*vertices, renderstate); }} First I attempt to retrieve the texture to verify that it exists (drawing textures that don't exist is not a good idea). Then I create a renderstate which will hold the rendering information. A renderstate is just a way to pass along information to the renderer - you might want to manipulate blending modes for example. Since I'm only interested in drawing rudimentary textures for now I simply set its texture datamember. Finally I pass the vertex array and renderstate to SFML which will take care of the drawing for me. Ok guys, that's it for now. Keep in mind that I am a relatively inexperienced game programmer which means that all the code in this journal should be taken with a grain of salt. If you think you know a better way to batch draw calls - you probably do and I'd love to hear about it in the comments section! Thanks for reading and as always you can download the code in its current state here.
  3. molehill mountaineer

    Arkanong development shots

    Screenshots detailing the development of my first game arkanong.
  4. molehill mountaineer

    Getting Games Done

  5. molehill mountaineer

    Your game is a dime a dozen

    You can't expect original ideas to spring fully formed in your mind - everything is a derivative in one way or another. Metal gear's stealth mechanic was a combination of technical restrictions and Hideo Kojima having just watched 'the great escape', for example.    I keep a notebook that I fill with clippings, quotes and the occasional drawing and make a point to look through it all regularly - the idea is that come into contact with things you wouldn't normally think of in order to make connections that you otherwise wouldn't have. One example might be that you're designing a character and come across a picture for a carburator which gives you the idea to give that character a mechanical heart.   As it happens, in this book there are a few quotes which are relevant to your issue:   "to find the answer one must first understand the question"   "extraordinary products are merely side effects of good habits"   "the object of painting a picture is not to make a picture - however unreasonable that may sound. The object, which is back of every true work of art, is the attainment of a state of being, a state of high functioning, a more than ordinary moment of existence. The picture is but a by-product of the state, a trace, the footprint of the state" (robert henri)   "I do not always know what I want, but I do know what I don't want.” (stanley kubrick)   ..by which I mean to say that you should just worry about making games for now. Then, when the game is done think about what you like and don't like about it and why. Then make the next game a little bit better. The quote by robert henri pertains to shutting off your inner critic and "going with the flow" - if you are criticizing your own work while you are making it you will also be afraid to play around and experiment, which will prevent you from gaining fresh perspectives.   Don't consider what you're making as something which other people have to like - think of it as a toy which you're messing around with and try to remember what was fun and what wasn't. Then use that for your next project.  Don't think of making something as "putting the good bits in" but as "taking the bad bits away". And as always the golden rule is to keep your nose to the grindstone - mastery takes practice.
  6. To make the game a bit more interesting we're going to add a few hexagonal barriers in the playing field. This object is different from the paddle and balls in that it doesn't have a 'regular' shape. This means that using a rectangular or circular hitregion will not cover the object properly. Now, I could fudge the dimensions of the hexagon a little and consider it a circle for the purposes of collision detection, but where's the fun in that? Luckily I found a neat little algorithm online which detects collisions between a circle and a line segment. Here's how it works: - make a vector which represents the line segment. We will call this vector d and form it by subtracting the start of the line segment from the end of the line segment. linevector = endPoint - startPoint => d = l - e - make a vector from the center of the circle to the start of the line segment. We'll call it vector f and form it by subtracting the circle's position from the start of the ray. We'll use this later on to simplify the equation. centerToStartPoint = start - center => f = e - c - combine the two following equations: a) p = e + t*d This equation locates the collision point on the line segment by starting from the first point (e) and 'moving' along the vector (d) by a certain percentage (t). In other words, if you have a line that starts at (4,0) and ends at (10,0), and a collision occurs in the middle of the line (= 50%) t will be 0.5 and p will be equal to (9,0). b) (x - h)[sup]2[/sup] + (y - k)[sup]2[/sup] = r[sup]2[/sup] This equation uses the pythagorean theorem to represent circle 'touching' the collision point. (h,k) is the center of the circle it basically means that if you take the distance from the collision point to the centre of the circle it will be equal to the radius (r) of the circle. Now we're going to combine these two equations to end up with our actual collision detection formula: 1 ) first expand equation number two. You'll end up with the following: x[sup]2[/sup] - 2xh + h[sup]2[/sup] + y[sup]2[/sup] - 2yk + k[sup]2[/sup] - r[sup]2[/sup] = 0 2) now, in the first equation the points p, e and vector d consists of two components (x and y). So we 'split' it into: x = e[sub]x[/sub] + td[sub]x[/sub] y = e[sub]y[/sub] + td[sub]y[/sub] Then you substitute the x and y variables in step 1 with these ones. You'll end up with this: ( e[sub]x[/sub] + td[sub]x[/sub] )[sup]2[/sup] - 2( e[sub]x[/sub] + td[sub]x[/sub] )h + h[sup]2[/sup] + ( e[sub]y[/sub] + td[sub]y[/sub] )[sup]2[/sup] - 2( e[sub]y[/sub] + td[sub]y[/sub] )k + k[sup]2[/sup] - r[sup]2[/sup] = 0 expand => e[sub]x[/sub][sup]2[/sup] + 2e[sub]x[/sub]td[sub]x[/sub] + t[sup]2[/sup]d[sub]x[/sub][sup]2[/sup] - 2e[sub]x[/sub]h - 2td[sub]x[/sub]h + h[sup]2[/sup] + e[sub]y[/sub][sup]2[/sup] + 2e[sub]y[/sub]td[sub]y[/sub] + t[sup]2[/sup]d[sub]y[/sub][sup]2[/sup] - 2e[sub]y[/sub]k - 2td[sub]y[/sub]k + k[sup]2[/sup] - r[sup]2[/sup] = 0 group => t[sup]2[/sup]( d[sub]x[/sub][sup]2[/sup] + d[sub]y[/sub][sup]2[/sup] ) + 2t( e[sub]x[/sub]d[sub]x[/sub] + e[sub]y[/sub]d[sub]y[/sub] - d[sub]x[/sub]h - d[sub]y[/sub]k ) + e[sub]x[/sub][sup]2[/sup] + e[sub]y[/sub][sup]2[/sup] - 2e[sub]x[/sub]h - 2e[sub]y[/sub]k + h[sup]2[/sup] + k[sup]2[/sup] - r[sup]2[/sup] = 0 write as dotproducts, where d = line segment, c = centre of circle => t[sup]2[/sup]( d DOT d ) + 2t( e DOT d - d DOT c ) + _e DOT _e - 2( _e DOT_c ) + _c DOT _c - r[sup]2[/sup] = 0 => t[sup]2[/sup]( d DOT d ) + 2t( d DOT ( e - c ) ) + ( e - c ) DOT ( e - c ) - r[sup]2[/sup] = 0 remember that e is the starting point of the line segment, and that we have a vector f which is the vector from the start of the line to the center of the circle (f = e - c) -> we can use this to further simplify the equation => t[sup]2[/sup]( d DOT d ) + 2t( d DOT f ) + f DOT f - r[sup]2[/sup] = 0 After we've simplified the formula as much as possible we ended up with the following: t[sup]2[/sup] * (d DOT d) + 2t*( f DOT d ) + ( f DOT f - r[sup]2[/sup] ) = 0 where: f is the vector from the centre of the circle to the starting point of the ray d is the vector representing the line itself r is the radius of the circle t is the 'percentage' of the line at which is the collision occurs If you look closely, you will see that this equation is of the form where: a = d DOT D b = 2 * f DOT d c = f DOT f - r^2 This is a quadratic equation, which means we can solve it by using the quadratic formula!sf::Vector2f lineVector = p_rayEnd - p_rayStart; sf::Vector2f centreToStartOfRay = p_rayStart - ballPosition; float a = (lineVector.x * lineVector.x) + (lineVector.y * lineVector.y); float b = (2 * ((centreToStartOfRay.x * lineVector.x) + (centreToStartOfRay.y * lineVector.y))); float c = ((centreToStartOfRay.x * centreToStartOfRay.x) + (centreToStartOfRay.y * centreToStartOfRay.y)) - (ballRadius * ballRadius); //calculate discriminant to determine if collision has taken place float discriminant = (b*b) - 4 * a*c; If the discriminant if greater than or equal to zero, then the circle has collided with the ray at some point. Calculate the solution to the equation by using the quadratic formula and you will end up with the 'percentage' values which in turn can be used to find the coordinates of the collision point. Now, these calculations consider the line as being infinitely long so we need to check if these percentages are between 0 and 1 (= 0% to 100% of the line). If they are greater or lesser than these values then the collision was either before or after the two points which make up the line segment. //one or more collision points with entire ray //(meaning also past the actual corners of the hexagon) if (discriminant >= 0) { //determine t1 & t2 //(this is the 'percentage' along the ray at which the collision point can be found) discriminant = sqrt(discriminant); float t1 = (-b - discriminant) / (2 * a); float t2 = (-b + discriminant) / (2 * a); //if the 'percentages' are between 0 and 1 there are //one or more collisions points within the actual line segment //(meaning contained between the two corners) if ((t1 >= 0 && t1 = 0 && t2
  7. molehill mountaineer

    Programmers talk "Alien"

    Ah my mistake, they used to be free but it seems they switched to a subscription format - pity!   There's still the whole wide internet at your disposal though, here are a few links to help you get started:  (remember that shopping around for a resource that suits your learning style is part of the process so always keeps google at the ready)   http://en.wikiversity.org/wiki/Introduction_to_Computer_Science http://math.hws.edu/eck/cs124/javanotes6/ http://www.deansdirectortutorials.com/Lingo/IntroductionToProgramming.pdf     EDIT: I see that you've clarified your initial question. Learning how to write an engine is mostly writing the code that you would be able to reuse in your next project (your next game will also need input, sound, graphics, etc -- this code is called the engine because it 'drives' the game and is usually seperated from the game logic to make it easier to reuse).   So, rather than google "how to build a game engine" you should take a look at tutorials for graphics libraries (directx, opengl, sfml, sdl, ...) since this is primarily what your engine will be doing. If you click the link for the directx tutorials below you will see example code that runs a gameloop, constructs a window and draws stuff onscreen - just add input and sound and you've got an engine! http://www.directxtutorial.com/LessonList.aspx?listid=11   Obviously I'm simplifying things a bit here - constructing an engine also requires careful planning (if your code is a mess and the game code isn't seperated properly you will have a difficult time reusing the code).     Now I wouldn't recommend directX for a beginner, since it can be quite daunting. In fact I would forget about making an engine altogether and focus first on learning the principles of good software design. This means learning about datatypes, design patterns and writing a few small programs to get a hang of them.   http://www.tenouk.com/Module2.html http://sourcemaking.com/design_patterns http://www.oodesign.com/     Once you feel confident that you know how to write good code and structure it logically it's time to make a few small games using beginner-friendly API's such as SFML. This will enable you to get familiar with how to use a game engine without having to actually write out everything. This step is very important! I did it the dumb way and just started writing a directX engine - the result is it took me way too much time, the design was sloppy and the performance wasn't great. Not very motivating I can tell you! Yes, I learned alot but I could have used that time to make 6 games in SFML so trust me on this one.   http://www.sfml-dev.org/tutorials/2.1/ http://www.gamefromscratch.com/page/Game-From-Scratch-CPP-Edition.aspx   After writing a few games you will have some experience under your belt and now you can start thinking about how to write your own engine. Start by taking a look at the source code of other engines (SFML is open source, for a start) and planning everything out on a piece of paper (this is where your experience with design patterns is vitally important and it's also the reason you shouldn't make engines unless you've made few games - how would you know which classes the engine needs if you don't know how to use one?).     EDIT: also, don't worry too much about doing things the most efficient way possible your first go around - get it to work first. Your only goal for now should be to get the code to do what it's supposed to, the rest will come naturally from experience. I have never made a program that didn't require some googling and self-education along the way.
  8. molehill mountaineer

    Programmers talk "Alien"

    If you're going to learn about computing you should get used to abbreviations because you're going to have to get to know a boatload of them (dslam, lan, isp, tcp, udp, rfc, gui, cli, the list is seemingly endless).   I can understand why you would find it frustrating but there is a good reason why people use this "alien language" - if I say to somebody "I made my main engine class a singleton" I can describe in 7 words what's going on rather than the more difficult to understand "I made a class which serves as an central administrative point for all subsystems with a private constructor, it can have only one instance and  is globally accessible". Don't worry though, you live in an age where things can be researched in a matter of seconds (programmers in the eighties had to use paper manuals, now that's a pain!). So google everything you don't understand and if you still don't get it try the gamedev chat, it's full of knowledgeable people who will be happy to help you.   Also, if you're going to ask questions you should try to be a bit more precise: what terms don't you understand and what should the tutorials you're asking for be about? If you're looking for introductions to programming you could try udacity.com (intro to computer science would be my choice).
  9. Well, it certainly has been a while since I updated this journal! Now that my school projects have been handed in and all my exams are behind me I will hopefully be able to spend more time developing arkanong. Let's take a look at how we can make the gameballs bounce off each other. There are three important things to keep in mind about our game physics: a) all balls have the same mass, meaning they all "weigh" the same. b) all collisions are perfectly elastic, so no energy will be converted to another form. This means that after two balls bounce off each other they will retain their kinetic energy (in real life part of it would be converted to sound waves, heat, etcetera). c) all movements/collisions are frictionless In a real pool game the friction of the felt would cause the ball to come to a halt after a while. We want our gameballs to keep moving indefinitely so our physics will be completely frictionless These three properties enable us to greatly simplify our physics formulas, as you will see further on. The sphere to sphere collision detection/response consists of the following steps: 1) detect a collision between two balls. This step is pretty easy; we simply check if the distance between two balls is less than (or equal to) the sum of their radiuses. The meaning of collision normal will be explained in step two, for now just think of it as the line that connects the two centres of the ball. The distance between the two balls is calculated using the pythagorean theorem.float radius1 = ball1->getSprite().getLocalBounds().width / 2;float radius2 = ball2->getSprite().getLocalBounds().width / 2;//minimumDistance for collision = radius1 + radius2float minimumDistance = radius1 + radius2;//amount of pixels between the centresfloat actualDistance = sqrt((collisionNormal.x * collisionNormal.x) + (collisionNormal.y * collisionNormal.y));//if the distance getVelocities();double ball2NormalComponent = (collisionNormal.x * ball2Velocity.x) + (collisionNormal.y * ball2Velocity.y);double ball2TangentComponent = (unitTangent.x * ball2Velocity.x) + (unitTangent.y * ball2Velocity.y); 5) Adjust the normal components of the ball velocities. As stated in the previous step, the velocity has a component that isn't affected (= tangent) and a component that is (= collision normal). In this step, we adjust the part of the velocity vector that will react to the collision, in other words: this is where the collision response is actually occuring. In Newtonian physics the adjusted normal velocities of two colliding spheres is calculated with the following formulas: where V is the new normal velocity after the collision, U is the old normal velocity before the collision, and M is the mass of the ball. Remember when I said that all balls "weigh" the same? This is where that knowledge comes in handy for reducing the complexity of the physics. Substitute m1 and m2 with the number 1 (or any number for that matter) and see what happens: V1 = (U1 * 0) + (2/2) * U2 V1 = 0 + 1 * U2 V1 = U2 you can just switch the two normal components!//-----------------switch normal components---------------//mass is the same for all balls, so the collision response function can be greatly simplified.//we simply switch the components.double newBall1Component = ball2NormalComponent;double newBall2Component = ball1NormalComponent; 6) multiply the components with the two unit vectors. By calculating the two components (normal & tangent) we now have the amount of pixels by which the ball should shift along these directions (you could think of this as "5 pixels and 10 pixels [up]", but again it's really the part of the velocity that is and isn't affected by the collision). These two numbers correspond to the magnitude of the new velocity vectors ("5 pixels and 10 pixels"), but we still need convert them into actual vectors via multiplication. (that is, give them a direction or make it "5 pixels to the left and 10 pixels up"). Actually we only need the normal part, because the tangent component is unaffected by the collision. I included the calculation anyway.//---------------convert components back into vectors----------sf::Vector2f newBall1NormalVector(0.0f, 0.0f);newBall1NormalVector.x = newBall1Component * collisionNormal.x;newBall1NormalVector.y = newBall1Component * collisionNormal.y;//these actually remain the same as before but we leave the calculation in for claritysf::Vector2f newBall1TangentVector(0.0f, 0.0f);newBall1TangentVector.x = ball1TangentComponent * unitTangent.x;newBall1TangentVector.y = ball1TangentComponent * unitTangent.y;sf::Vector2f newBall2NormalVector(0.0f, 0.0f);newBall2NormalVector.x = newBall2Component * collisionNormal.x;newBall2NormalVector.y = newBall2Component * collisionNormal.y;//these actually remain the same as before but we leave the calculation in for claritysf::Vector2f newBall2TangentVector(0.0f, 0.0f);newBall2TangentVector.x = ball2TangentComponent * unitTangent.x;newBall2TangentVector.y = ball2TangentComponent * unitTangent.y; 7) add the components back together. Earlier we decomposed the velocity of the balls into two components (normal & tangent), now that we've recalculated these two components we simply add them back together to get the new velocity for the ball. ('5 to the left and 10 up' becomes '14 diagonally')sf::Vector2f newBall1Velocity = newBall1TangentVector + newBall1NormalVector;sf::Vector2f newBall2Velocity = newBall2TangentVector + newBall2NormalVector; Now, because I store angles rather than velocity vectors I do some additional trigonometry on these but if you work with vectors in your code then you're as good as done, all that needs doing is setting the new directions for the balls. I realize this is probably confusing as hell if you're not familiar with physics or vector mathematics. Check out these links if you want to get a better grasp of what's going on here - sooner or later every game programmer is going to come into contact with these things so it's better to start early. http://en.wikipedia.org/wiki/Elastic_collision http://www.vobarian.com/collisions/2dcollisions2.pdf http://nicoschertler.wordpress.com/2013/10/07/elastic-collision-of-circles-and-spheres/ As always you can check out the code for the game on github. The code shown in this article can be found in source -> ObjectManager.cpp -> performCircleToCirlceCollisionChecks()
  10. molehill mountaineer

    A bit about my game and some slow progress

    neat, I was just thinking about how best to batch draw calls in SFML - It didn't occur to me that the texture can be batched, leaving the transformations intact inside a Sprite instance. Thanks!
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!