Jump to content
Site Stability Read more... ×
  • Advertisement

Search the Community

Showing results for tags 'Concept'.

The search index is currently processing. Current results may not be complete.


More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Categories

  • Audio
    • Music and Sound FX
  • Business
    • Business and Law
    • Career Development
    • Production and Management
  • Game Design
    • Game Design and Theory
    • Writing for Games
    • UX for Games
  • Industry
    • Interviews
    • Event Coverage
  • Programming
    • Artificial Intelligence
    • General and Gameplay Programming
    • Graphics and GPU Programming
    • Engines and Middleware
    • Math and Physics
    • Networking and Multiplayer
  • Visual Arts
  • Archive

Categories

  • Audio
  • Visual Arts
  • Programming
  • Writing

Categories

  • Game Dev Loadout
  • Game Dev Unchained

Categories

  • Game Developers Conference
    • GDC 2017
    • GDC 2018
  • Power-Up Digital Games Conference
    • PDGC I: Words of Wisdom
    • PDGC II: The Devs Strike Back
    • PDGC III: Syntax Error

Forums

  • Audio
    • Music and Sound FX
  • Business
    • Games Career Development
    • Production and Management
    • Games Business and Law
  • Game Design
    • Game Design and Theory
    • Writing for Games
  • Programming
    • Artificial Intelligence
    • Engines and Middleware
    • General and Gameplay Programming
    • Graphics and GPU Programming
    • Math and Physics
    • Networking and Multiplayer
  • Visual Arts
    • 2D and 3D Art
    • Art Critique and Feedback
  • Community
    • GameDev Challenges
    • GDNet+ Member Forum
    • GDNet Lounge
    • GDNet Comments, Suggestions, and Ideas
    • Coding Horrors
    • Your Announcements
    • Hobby Project Classifieds
    • Indie Showcase
    • Article Writing
  • Affiliates
    • NeHe Productions
    • AngelCode
  • Topical
    • Virtual and Augmented Reality
    • News
  • Workshops
    • C# Workshop
    • CPP Workshop
    • Freehand Drawing Workshop
    • Hands-On Interactive Game Development
    • SICP Workshop
    • XNA 4.0 Workshop
  • Archive
    • Topical
    • Affiliates
    • Contests
    • Technical
  • GameDev Challenges's Topics
  • For Beginners's Forum
  • Unreal Engine Users's Unreal Engine Group Forum
  • Unity Developers's Forum
  • Unity Developers's Asset Share

Calendars

  • Community Calendar
  • Games Industry Events
  • Game Jams
  • GameDev Challenges's Schedule

Blogs

There are no results to display.

There are no results to display.

Product Groups

  • Advertisements
  • GameDev Gear

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


About Me


Website


Role


Twitter


Github


Twitch


Steam

Found 111 results

  1. Tomato Head

    Discarding Ideas

    Hello everybody, I'm very interested in learning what is your process for discarding core game concepts that you've have come up with. Knowing that it is very hard to work on more that one development at once and plenty of people have more than one idea for games in their head, how do you discard or prioritize your concepts? Have many of you created prototypes and only then found the concept is not good? Have you found that there are core concepts that are bad and can never be iterated to get a playable/good game? Any advice is very much welcomed.
  2. Hi, I'm not sure if this is the right forum for this, or it should go to General Programming. Please feel free to move it. I'd like to read skeleton from a model, using Assimp. As it turned out, interpreting Assimp structures is just as complicated as parsing many different formats at once. Judging by huge number of assimp-bone related questions on forums (some of them here too) I'm not alone with this problem. I've searched a lot, and I could find many answers to my questions, however the pieces are still not fitting together entirely. To explain what my problem is, I'll try to summarize what I've gathered so far, and please correct me if I'm wrong somewhere. The main problem is, both bone structure and mesh structure is stored in the same node structure, however it would be false to assume they are correlated. Meaning you have to traverse the same node-tree two times, completely independently, to get the correct results: one time for the meshes, and one time for the bones. Is this true? The best instructions I could find so far is here under section "Bones", but it is just a pseudo-description of a rather ridiculous algorithm (I'm sorry, but I really think that). Some important answers I've found here. Most assimp tutorials (like here and here) I've found seems to miss key parts of the whole procedure. They usually simply iterate through aiScene->mMeshes or walking the node tree collecting mMeshes in a vector, but from what I've learned so far, this is wrong (or more precisely only works in an exceptional case when only the root node has meshes). To explain what I mean: - model space: this is what we use to display our model - mesh node space: all mMeshes in a node contains vertices in this space (is this correct? or are they stored in model space in the first place?) - bone node space: similar to mesh node space, however totally unrelated to the node space that contains the mesh So, to correctly load all vertices into model space, we have walk through the node tree, concatenating the node's transformation matrix along the path and apply it to the vertices. Otherwise if there's only one node with meshes, and it's the root node, then, and only then model space == mesh node space. Is this correct? Bone nodes' transformation matrices are not model space related, rather skeleton hierarchy related, and they also have an offset matrix which transforms from mesh node space into this bone's node space. This is clear (at least something is :-)) Traversing the node tree for bones and concatenating their transformation matrices along the path will result in a matrix that converts from bone node space into model space. If there's only one node with meshes and that's the root node, then, and only then this concatenated matrix is the inverse of the offset matrix. This seems reasonable if I understood everything correctly. If we later want to use an animation, then we have to 1. get the list of bones which changed on that particular frame 2. collect all vertices that are affected by that bone (mMeshes[]->aiBone) 3. collect all of the bones that control those vertices and all of those bone's children (in a unique list, as we have to recalculate all vertices belonging to those bones) 4. using the corresponding offset matrices, convert those vertices from their "bind-pose" skeleton mesh node space into one or more bone node spaces ( V -> V'[1..numWeights]) 5. use the transformation required by the animation frame on all vertices that belong to the modified bone using their corresponding bone node space versions (V'[x]), or do we multiply the frame transformation matrix temporarily with the bone's transformation matrix? (In other words, should we transform the vertices in the bone space or their coordinate system in model space?) 6. get a weighted average of each vertex in their corresponding bone node space (w[1..numWeights] * V'[1..numWeights] -> Vm), then transform the result into model space (or should we / would it be better to convert the points into model space first and calculate the weighted average there? (Let's assume we have a frame for the sake of simplicity, I know how to iterate skeletons.) A little note on 5th question: although it seems to be irrelevant whether we transform the points or their coordinate system, because we'll get the same result (in model space), however this affects the points of the children bone spaces differently. I guess we must not convert the bone node spaces into model spaces, rather keep them parent bone node space relative, and only convert the final points back to model space. Am I correct? Thanks, bzt
  3. Loosearmy

    Delayed shots Concept

    Concept for Delayed Shots in a Fast Paced Shooter The base for this concept is that with each click or trigger pull there is a X-second delay before the gun would actually fire. This would make it alot more difficult to time shots and could create unique design elements that would cater to this delay. (i.e sharp corners and hallways where it would be hard to time when to click in such a tight enclosed space). Ive had this concept for a minute and i know we could code it to work but my main concern with this is, would it be a good design choice?
  4. In PixelCast 7, Jeremy hangs out at Pixels = Pints + Bytes for the latest PixelFest Devs meetup and chats with two local indies about their studios. Joshua Jané demos 'Bouncy Bear' and explains what makes 'Just Bare Games' tick, including the fact that all their games contain bears! Meanwhile, Joseph Musso of Sunset Studios let's us in on the game he's been pondering for 10 years ('Santa's Sleigh Ride Sacrilegious Arcade Action'... say it three times fast), which is now playable after coming out to a PixelFest Devs meetup back in August. View full story
  5. In PixelCast 7, Jeremy hangs out at Pixels = Pints + Bytes for the latest PixelFest Devs meetup and chats with two local indies about their studios. Joshua Jané demos 'Bouncy Bear' and explains what makes 'Just Bare Games' tick, including the fact that all their games contain bears! Meanwhile, Joseph Musso of Sunset Studios let's us in on the game he's been pondering for 10 years ('Santa's Sleigh Ride Sacrilegious Arcade Action'... say it three times fast), which is now playable after coming out to a PixelFest Devs meetup back in August.
  6. After loading a level, I now propose to add interaction with the use of a mouse. This will be an opportunity to see two other patterns: the Observer Pattern to handle mouse events, and the Game Loop Pattern for synchronization between controls, updates, and display. This post is part of the AWT GUI Facade series Mouse Facade To add the management of the mouse to the facade, I propose to introduce a new Mouse interface that contains all the functions related to the mouse: Separating the interface for the mouse from the general interface of the facade has two advantages: lighten the general interface and allow the simultaneous management of several mice. As far as methods are concerned, I chose the simplest API possible: isButtonPressed() method: returns true if a button is pressed; getX() and getY() methods: return the (x, y) coordinates of the cursor in the window This API is also lowlevel, to match what most graphic libraries offer. In addition, it allows having at any time the complete state of the mouse, which is often necessary for video games. For the general GUIFacade interface, a new getMouse() method is added to return an implementation of the Mouse interface. Mouse handling in AWT To implement this interface, AWT library included in the standard Java library is still used. This one offers a high-level API, which meets the needs of office applications. It is based on the Observer Pattern, which allows an element to observe (or listen to) another element, and to be notified when an event occurs. In the case of the mouse, these events are for example the pressure of a button or the movement of the cursor. This API is divided into several interfaces, for example, the interface MouseListener allows to manage the events related to the buttons: The MouseListener interface contains the methods implemented by the observer: for example, when a button is pressed, the mousePressed() method is called and the observer can then act accordingly. The Component class, which is the superclass of many graphic components within AWT, can be observed by anyone who requests it thanks to the addMouseListener() method. For the example of this article, it is the Canvas used to make the level of the game that is observed: each action of the mouse in its display area causes calls to the methods of the MouseListener interface. Facade implementation I propose that the implementation of the Mouse interface of the facade takes the form of an AWTMouse class: The class implements the Mouse, MouseListener, and MouseMotionListener interfaces. The first methods provide information about the mouse (contained in its attributes): public class AWTMouse implements Mouse, MouseListener, MouseMotionListener { private final boolean[] buttons; private int x; private int y; public AWTMouse() { buttons = new boolean[4]; } @Override public boolean isButtonPressed(int button) { if (button >= buttons.length) { return false; } return buttons[button]; } @Override public int getX() { return x; } @Override public int getY() { return y; } ... The following ones respond to mouse events and update mouse information: @Override public void mouseClicked(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { if (e.getButton() <= 3) { buttons[e.getButton()] = true; } } @Override public void mouseReleased(MouseEvent e) { if (e.getButton() <= 3) { buttons[e.getButton()] = false; } } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } @Override public void mouseDragged(MouseEvent e) { x = e.getX(); y = e.getY(); } @Override public void mouseMoved(MouseEvent e) { x = e.getX(); y = e.getY(); } } Finally, the AWTWindow class contains as before the canvas, and this one is used to “listen” the events of the mouse: public void init(String title, int width, int height) { ... mouse = new AWTMouse(); canvas.addMouseListener(mouse); canvas.addMouseMotionListener(mouse); } Game Loop It only remains to exploit this new interface in an example. To do this, I propose to introduce the Game Loop Pattern in its simplest version (without multi-threaded considerations). It is based on a set of methods that can be grouped together in the same class: The init() method is called at startup to initialize the game and its data; The processInput() method is called at each iteration of the game to manage the controls (keyboard, mouse, …). In short, its main role is to transform the user’s “instructions” into more abstract “orders” or “commands” that the game engine knows how to interpret. Considering time, these operations go to the rhythm of the user. The update() method applies changes to game data based on various sources, such as commands produced by user controls or operations that must be applied to each update. Considering time, these operations go to the speed of the game engine. The render() method handles display. This method will most often order or transfer data on the graphics card, the latter dealing with very low-level tasks at the pixel level. Considering time, these operations go to the refresh rate of the screen (default 60Hz). The run() method calls the previous ones and contains the actual game loop. In this example, I don’t implement all the elements required to meet all these principles. I propose here a basic form of this pattern, sufficient to begin to understand all these notions. For the init() method, we build the two layers and initialize the window: public void init(Level level) { this.level = level; backgroundLayer = gui.createLayer(); backgroundLayer.setTileSize(level.getTileWidth(),level.getTileHeight()); backgroundLayer.setTexture(level.getTilesetImage(0)); backgroundLayer.setSpriteCount(level.getWidth()*level.getHeight()); groundLayer = gui.createLayer(); groundLayer.setTileSize(level.getTileWidth(),level.getTileHeight()); groundLayer.setTexture(level.getTilesetImage(1)); groundLayer.setSpriteCount(level.getWidth()*level.getHeight()); gui.createWindow("Exemple de contrôle avec la souris", scale*level.getTileWidth()*level.getWidth(), scale*level.getTileHeight()*level.getHeight()); } For the processInput() method, if the following conditions are met: the left button is pressed; there is a cell of the level under the cursor; the tile of this cell is in the second set of tiles (the one of the second layer), then we put a tile with grass (this will erase all buildings in the map): public void processInput() { Mouse mouse = gui.getMouse(); if (mouse.isButtonPressed(MouseEvent.BUTTON1)) { int x = mouse.getX() / (scale*level.getTileWidth()); int y = mouse.getY() / (scale*level.getTileHeight()); if (x >= 0 && x < level.getWidth() && y >= 0 && y < level.getHeight()) { if (level.getTileset(x,y) == 1) { level.setTileset(x,y,0); level.setTile(x,y,new Point(7,0)); } } } } For the update() method, the content of the level data is used to define the sprite textures. It is the same as in the previous post, except that this operation is repeated regularly: public void update() { for(int y=0;y<level.getHeight();y++) { for(int x=0;x<level.getWidth();x++) { int index = x + y * level.getWidth(); backgroundLayer.setSpriteLocation(index, new Rectangle(scale*x*level.getTileWidth(), scale*y*level.getTileHeight(), scale*level.getTileWidth(), scale*level.getTileHeight())); if (level.getTileset(x, y) == 0) { Rectangle tile = new Rectangle(level.getTile(x, y), new Dimension(1,1)); backgroundLayer.setSpriteTexture(index, tile); } else { backgroundLayer.setSpriteTexture(index, null); } } } for(int y=0;y<level.getHeight();y++) { for(int x=0;x<level.getWidth();x++) { int index = x + y * level.getWidth(); groundLayer.setSpriteLocation(index, new Rectangle(scale*x*level.getTileWidth(), scale*(y-1)*level.getTileHeight(), scale*level.getTileWidth(), scale*2*level.getTileHeight())); if (level.getTileset(x, y) == 1) { Rectangle tile = new Rectangle(level.getTile(x, y), new Dimension(1,2)); groundLayer.setSpriteTexture(index, tile); } else { groundLayer.setSpriteTexture(index, null); } } } } The render() method just draws the two layers: public void render() { if (gui.beginPaint()) { gui.drawLayer(backgroundLayer); gui.drawLayer(groundLayer); gui.endPaint(); } } Finally, the run() method contains the game loop that calls the other methods at most 60 times per second: public void run() { int fps = 60; long nanoPerFrame = (long) (1000000000.0 / fps); long lastTime = 0; while(!gui.isClosingRequested()) { long nowTime = System.nanoTime(); if ((nowTime-lastTime) < nanoPerFrame) { continue; } lastTime = nowTime; processInput(); update(); render(); long elapsed = System.nanoTime() - lastTime; long milliSleep = (nanoPerFrame - elapsed) / 1000000; if (milliSleep > 0) { try { Thread.sleep (milliSleep); } catch (InterruptedException ex) { ex.printStackTrace(); } } } gui.dispose(); } This basic implementation of the Game Loop pattern does not respect all its principles. There is no real notion of control, and it will be difficult to run each part at different rates. In addition, in terms of design, there are also questionable choices, like the Level class that serves as both a level loader, storage for the level, and a form of buffer. Through future posts, I will present all that is needed to achieve more effective implementations. The code of this post can be downloaded here: awtfacade06.zip To compile: javac com/learngameprog/awtfacade06/Main.java To run: java com.learngameprog.awtfacade06.Main The post AWT GUI Facade (6): Mouse and Game Loop appeared first on Design Patterns and Video Games. View the full article
  7. Tony Vilgotsky

    What makes a game an "indie" game?

    Hello, dear colleagues! Recently I had a long evening of reflections on the topic, what makes a video game an indie game? Of course, indie projects are the whole niche in this days, but it seems sometimes that when some people are talking about “indie games”, they just don’t know what they are talking about. For some reason many people think that being indie means to be a low-qualified person who is obligated to produce tons of clones of classic games which were hits decades ago. And when they come to game making, they even don’t consider an opportunity to create something more original and interesting. They deliberately produce all these clones. Moreover, with deliberately downgraded graphics, sound and mechanics, which makes sense when it comes to retro scene, but… retro scene is a separate scene and all the indie games don’t have to fit its standards. And then they complain: why nobody is buying this? I think that the answer is obvious: modern gamers prefer modern graphics. So why shoot your own leg, filling your game with pixels, which aren’t even “an art”? I’m even silent about the gameplay clones. I think that there should be fewer of them, because indie development is just that thing that gives you freedom to be more original and implement bolder decisions. The world is playing Tetris for 200 years already (correct me if I’m wrong) - and nobody wants to play Tetris with crocodiles instead of bricks. It’s sometimes even getting funny: recently I’ve joined a new team (as a junior writer), making a 3D action/adventure game Between Realms with Unreal engine. This is an indie team and indie game, but the goal is to produce a major hit. The game incorporates modern 3D graphics, cutscenes and professional voice overs. And when we posted some stuff from it on the Internet, some people just didn’t believe that we are actually an indie team! I think it’s because modern indie projects almost always belong to the pixel scene or notably simplified from the visual side. So, when we show our materials, some people don’t believe that we belong to indie niche. Does it mean that one has to produce simple games with poor 2D pixel graphics to be considered an indie developer? From my (and my colleagues’) point of view, indie development must be not a compromise between production speed, expense and quality, but a freedom to create a conceptual product which is not limited by obligatory to sell millions of copies. Those who are tied by this obligatory, are forced to produce standardized, so to say, “tentpole” projects. But with modern technologies the developer is able to create nice, commercially promising, but still an independent project. As developers, we want to raise the bar of indie standard and wish all the rest to reach the same with the games which would be not only original in terms of gameplay but also attractive visually. What are you thoughts on this matter? Can a project with budget over $100 be called an indie project? What are the main features of typical indie game? What really makes a game an “indie” game?
  8. I continue the extension of the graphic facade (see previous article), with here the addition of a very classic form of drawing for video games: tiles. These make it possible to compose an image using small images called tiles. There are several types of tiles, I propose in this article the simplest case with rectangular tiles. This post is part of the AWT GUI Facade series. For the implementation, the approach that I propose to follow is similar to the one used for images: we use a factory (Factory Method Pattern) to instantiate layers that allow drawing with tiles. On the GUIFacade main interface side, I add the following two methods, one to create a layer, and the other to draw it: For layers (Layer interface), I propose to manage the tiles with one texture image by layer, and with tiles of a fixed size. In addition, the rendering of tiles can be memorized by the layer: in doing so, it is sufficient to indicate which tiles should be displayed in which places, and then the layer will display these tiles continuously. These renderings are called in this article sprites, so as not to confuse them with tiles (e.g., a tile is just a small picture, and a sprite is a small picture displayed at a specific location on the screen). At this stage of design, layers able to renders independently may seem unnecessary: we could continuously consult the game data for rendering. Later, when it will be necessary to make multi-threaded synchronization, independent layers are a blessing. In addition, for low-level implementations (such as OpenGL), it allows you to use vertex buffers, which greatly speeds up rendering. These properties can be obtained via an interface like the following one: The getTileWidth() and getTileHeight() methods return the size of the tiles (for example, 16 by 16 pixels); The getTextureWidth() and getTextureHeight() methods return the number of tiles in the texture image, in width and height; The setTileSize() method is used to set the size of tiles; The setTexture() method is used to define and load the texture image; The setSpriteCount() method is used to set the number of sprites managed by the layer; The setSpriteTexture() method is used to define the tile used by a sprite. The tile rectangle defines the used tile: they are coordinates in tiles, and not in pixels. For example, if the tile size is 16×16 pixels, and the tile rectangle is (3,4,1,2), then the tile location in pixels is (3 * 16,4 * 16,1 * 16,2 * 16) = (48,64,16,32). By convention, if the rectangle argument is null, then the sprite is disabled; The setSpriteLocation() method is used to define the location where the sprite is drawn. The rectangle is in pixels, and may be larger or smaller than the tiles used, to achieve a zoom effect. AWT Implementation For the implementation, we still use the AWT library, with the AWTLayer class (the methods of the Layer interface are not repeated): The tileWidth, tileHeight, textureWidth, and textureHeight attributes define the size of the tiles and the texture image; The texture attribute contains the texture image; The texture and location arrays contain all sprite information: tiles to use and render locations; The draw() method draws the entire layer. Most methods are accessors / mutators (getters / setters) whose implementation is very simple. The draw () method is less obvious: public void draw(Graphics graphics) { for (int i = 0; i < locations.length; i++) { if (textures[i] != null) { graphics.drawImage(texture, locations[i].x, locations[i].y, locations[i].x + locations[i].width, locations[i].y + locations[i].height, textures[i].x * tileWidth, textures[i].y * tileHeight, (textures[i].x+textures[i].width) * tileWidth, (textures[i].y+textures[i].height) * tileHeight, null); } } } All sprites are traversed (l.2), if a sprite is well defined (l.3), we draw an image (l.4-13). Lines 5 to 8 define the rendering location, in pixels: it is the coordinates of the upper left corner and the lower right corner of the rectangle. Lines 9 to 12 define a rectangle in the texture image: all coordinates are multiplied by tile size (tileWidth, tileHeight). Use case To illustrate this facade with tile drawing, I propose to consider two layers: one for the background, and the other for the buildings. In both cases, we consider the same tile size of 16 × 16 pixels, and the same area of rendering (the level of the game) of 17 × 17 tiles. In addition, a scale factor is used to zoom tiles. These parameters are placed in variables: int scale = 2; int tileWidth = 16; int tileHeight = 16; int levelWidth = 17; int levelHeight = 17; For the background layer, each sprite uses only one tile, and we always use the same one: Layer backgroundLayer = gui.createLayer(); backgroundLayer.setTileSize(tileWidth,tileHeight); backgroundLayer.setTexture("advancewars-tileset1.png"); backgroundLayer.setSpriteCount(levelWidth*levelHeight); for(int y=0;y<levelHeight;y++) { for(int x=0;x<levelWidth;x++) { int index = x + y * levelWidth; backgroundLayer.setSpriteLocation(index, new Rectangle(scale*x*tileWidth, scale*y*tileHeight, scale*tileWidth, scale*tileHeight)); backgroundLayer.setSpriteTexture(index, new Rectangle(new Point(7,0), new Dimension(1,1))); } } Lines 1-4 instantiate and initialize the layer. Then, for each cell of the level (l, 5-6), we associate a sprite (l, 7). To achieve this association, it is necessary to define a unique number for each cell of the level: to do this, the cells are numbered from left to right then from top to bottom. The rendering area of each sprite is defined in lines 8-9, which is a simple scaling: everything is multiplied by the tileWidth, tileHeight, and the global scale. Finally, the tile is selected in lines 10-11, with the 8th column of the first line of the texture Point (7,0) and the size of a tile Dimension (1,1) The following layer is used to display buildings, with the particularity that these have a height of two tiles in height: Layer groundLayer = gui.createLayer(); groundLayer.setTileSize(tileWidth,tileHeight); groundLayer.setTexture("advancewars-tileset2.png"); groundLayer.setSpriteCount(2); groundLayer.setSpriteLocation(0, new Rectangle(scale*8*tileWidth, scale*6*tileHeight, scale*tileWidth, scale*2*tileHeight) ); groundLayer.setSpriteTexture(0, new Rectangle(new Point(0,2), new Dimension(1,2))); groundLayer.setSpriteLocation(1, new Rectangle(scale*8*tileWidth, scale*7*tileHeight, scale*tileWidth, scale*2*tileHeight) ); groundLayer.setSpriteTexture(1, new Rectangle(new Point(0,4), new Dimension(1,2))); Lines 1-4 instantiate the layer and initialize its properties: tile size, texture image, and sprite numbers (2). Lines 6 through 11 define the properties of the first sprite (index 0): its render location is at cell (8,6) of the grid (l. 7), its render size equals a tile width and two heights of tiles (l. 8). Its tiles are at coordinates (0,2) in the texture and use a tile of width one and height two (l. 11). Lines 13 to 18 define the properties of the second sprite: it is drawn on the cell just below, and its tiles start at the coordinates (0,4) in the texture. If all goes well, the top of the second building sprite should cover the bottom of the first: Finally, in the game’s main loop, displaying layers is as easy as displaying an image: if (gui.beginPaint()) { gui.drawLayer(backgroundLayer); gui.drawLayer(groundLayer); gui.endPaint(); } The code of this article can be downloaded here: awtfacade04.zip Some improvements have been made compared to the version of the previous article: it uses a canvas and limits the number of images per second. These improvements are purely related to the problems of video games, and does not change the concepts presented above. To compile: javac com/learngameprog/awtfacade04/Main.java To run: java com.learngameprog.awtfacade04.Main Next post in this series View the full article
  9. In this article, I propose to use the Visitor Pattern to easily load a level. The GUI facade in the previous post is used to display the level. This pattern allows (among other things) to easily browse a data structure to extract information. In our case, it is an XML file created by “Tiled Map Editor” which is analyzed to load a level in memory. This post is part of the AWT GUI Facade series I start by creating a level in XML with Tiled: You have to choose in the left panel Tile layer format as “XML”. This produces an XML file that looks like this: <map version="1.0" orientation="orthogonal" renderorder="left-up" width="16" height="16" tilewidth="16" tileheight="16" nextobjectid="1"> <tileset firstgid="1" name="advancewars-tileset1" tilewidth="16" tileheight="16" tilecount="256"> <image source="advancewars-tileset1.png" width="256" height="256"/> </tileset> <tileset firstgid="257" name="advancewars-tileset2" tilewidth="16" tileheight="16" tilecount="256"> <image source="advancewars-tileset2.png" width="256" height="256"/> </tileset> <layer name="Ground" width="16" height="16"> <data> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> ... <tile gid="41"/> </data> </layer> <layer name="Objects" width="16" height="16"> <data> <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> ... <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> </data> </layer> </map> We first have a <main> tag with global properties, then two <tileset> tags that define the tile sets used (one for the background, the other for the objects), and finally two <layer> tags that define the tiles for two layers (one for the background, the other for the objects). To decode this file, I propose to use the XML parser included in the standard Java library. It is based on the Visitor pattern, and allows you to easily work on tags without having to worry about character decoding issues. Visitor Pattern The Visitor pattern can be presented as follows: The IElement interface represents the elements of the structure. In this example, there are two possible types of objects: ElementA and ElementB. The interface requires a method like accept() which takes as argument a visitor who implements the IVisitor interface. There can be several ways to visit the structure, and so many other methods similar to accept(). Parameters can also be used to influence the traversal. The IVisitor interface is implemented by the user who wants to traverse the structured data. In general, the interface methods correspond to the different types of elements that compose it: in this example, they are the ElementA and ElementB classes. It is quite possible to imagine other cases that may interest a user, such as being at the beginning or at the end of an element. In all cases, the accept() method calls the methods of the IVisitor interface according to the cases encountered during its course. Within these methods, the user is free to view and modify the data. Load a level To read and store the level information, I define a Level class that contains this information in attributes: public class Level { private ArrayList<String> tilesetImages = new ArrayList(); private int tileWidth; private int tileHeight; private int width; private int height; private int[][][] level; private int tilesetWidth; private int tilesetHeight; private int x; private int y; ... The tilesetImages attribute contains the file names of texture images; The tileWidth and tileHeight attributes contain the width and height of a tile (16 x 16 pixels in our example); The width and height attributes contain the width and height of the level (16 x 16 cells in our example); The level attribute contains for each cell coordinate: the (x, y) tile coordinates of the tile to draw, and the tile set to use. The following attributes are only used to decode the information in the file. To decode the file, I define a LevelLoader class that implements org.xml.sax.helpers.DefaultHandler. The LevelLoader class is the equivalent of the Visitor class in the diagram above, and DefaultHandler is the equivalent of IVisitor. I’m only interested in the startElement() method, equivalent to the visitElementA() and visitElementB() methods in the diagram above. This method is called whenever the parser encounters a new opening tag: public class LevelLoader extends DefaultHandler { public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("tileset")) { if (tilesetImages.isEmpty()) { tileWidth = Integer.parseInt(attributes.getValue("tilewidth")); tileHeight = Integer.parseInt(attributes.getValue("tileheight")); } } else if (qName.equals("image")) { if (tilesetImages.isEmpty()) { tilesetWidth = Integer.parseInt(attributes.getValue("width")) / tileWidth; tilesetHeight = Integer.parseInt(attributes.getValue("height")) / tileHeight; } tilesetImages.add(attributes.getValue("source")); } else if (qName.equals("layer")) { if (level == null) { width = Integer.parseInt(attributes.getValue("width")); height = Integer.parseInt(attributes.getValue("height")); level = new int[height][width][3]; } x = 0; y = 0; } else if (qName.equals("tile")) { int id = Integer.parseInt(attributes.getValue("gid")); if (id != 0) { if (id >= 257) { level[x][y][2] = 1; id -= 256; level[x][y][0] = id % tilesetWidth - 1; level[x][y][1] = id / tilesetWidth - 1; } else { level[x][y][0] = id % tilesetWidth - 1; level[x][y][1] = id / tilesetWidth; } } x ++; if (x >= width) { x = 0; y ++; if (y > height) { throw new SAXException("Erreur dans le fichier"); } } } } } The method is a long discussion based on the tag encountered, whose name is placed in the qName argument. For the “tileset” and “image” cases the information relating to a set of tiles is decoded. For the “layer” case, the level is initialized, and for “tile”, the information relating to a tile is stored. The x and y attributes are used to store the coordinates of the next tile to be decoded. Finally, I define a load() method in the Level class that uses the LevelLoader class to load the level: public boolean load(String fileName) { try { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); xmlReader.setContentHandler(new LevelLoader()); URL fileURL = this.getClass().getClassLoader().getResource(fileName); xmlReader.parse(fileURL.toString()); return true; } catch(Exception ex) { return false; } } The Level class also has accessors/mutators (getters/setters) not listed here. Display the level To display the level, I use the GUI facade from the previous post: Level level = new Level(); if (!level.load("advancewars-map1.tmx")) { JOptionPane.showMessageDialog(null, "Error when loading advancewars-map1.tmx", "Error", JOptionPane.ERROR_MESSAGE); return; } int scale = 2; Layer backgroundLayer = gui.createLayer(); backgroundLayer.setTileSize(level.getTileWidth(),level.getTileHeight()); backgroundLayer.setTexture(level.getTilesetImage(0)); backgroundLayer.setSpriteCount(level.getWidth()*level.getHeight()); for(int y=0;y<level.getHeight();y++) { for(int x=0;x<level.getWidth();x++) { int index = x + y * level.getWidth(); backgroundLayer.setSpriteLocation(index, new Rectangle(scale*x*level.getTileWidth(), scale*y*level.getTileHeight(), scale*level.getTileWidth(), scale*level.getTileHeight())); if (level.getTileset(x, y) == 0) { Rectangle tile = new Rectangle(level.getTile(x, y), new Dimension(1,1)); backgroundLayer.setSpriteTexture(index, tile); } } } Layer groundLayer = gui.createLayer(); groundLayer.setTileSize(level.getTileWidth(),level.getTileHeight()); groundLayer.setTexture(level.getTilesetImage(1)); groundLayer.setSpriteCount(level.getWidth()*level.getHeight()); for(int y=0;y<level.getHeight();y++) { for(int x=0;x<level.getWidth();x++) { int index = x + y * level.getWidth(); groundLayer.setSpriteLocation(index, new Rectangle(scale*x*level.getTileWidth(), scale*(y-1)*level.getTileHeight(), scale*level.getTileWidth(), scale*2*level.getTileHeight())); if (level.getTileset(x, y) == 1) { Rectangle tile = new Rectangle(level.getTile(x, y), new Dimension(1,2)); groundLayer.setSpriteTexture(index, tile); } } } gui.createWindow("Load a level with the Visitor Pattern", scale*level.getTileWidth()*level.getWidth(), scale*level.getTileHeight()*level.getHeight()); The code of this post can be downloaded here: Download Now! To compile: javac com/learngameprog/awtfacade05/Main.java To run: java com.learngameprog.awtfacade05.Main View the full article
  10. Hello and welcome to this weeks Dev Diary! Today I'll be continuing on how upscaling heightmaps affects the end result, when you wan't to create more detailed terrain with ease. After last weeks test with the heightmap upscales, I decided to try to upscale the heigthmap even further - from 3000 x 3000 pixels to 40 000 x 40 000 pixels, just to see if there would be any return on investment. Or in other words, if there would be any benefit from the extra detail, that the upscaling algorithm creates. Initial Testing First, lets compare the first set of renders. Like last week, all the settings remain unchanged between renders and only the heightmap is changed between them. This is done in order to eliminate the changes that changing the the lighting or camera angle could cause by creating/showing shadows in different perspective, if moved between renders. Plane 8m x 8m in size. Subdivided several times, first by 100, second by 6 and third by 1 (or 100:6:1 subdivision) Diffuse Scale 1.0, midpoint 0.5 (defaults) Resolutions from top to bottom; 750 x 750, 3000 x 3000, 40 000 x 40 000. The above 750 x 750 pixel heightmap sample is from the original 3000 x 3000 heightmap. The above 3000 x 3000 pixel heightmap sample is from the upscaled 12 000 x 12 000 pixel heightmap. The above 10 000 x 10 000 pixel heightmap sample is from the upscaled 40 000 x 40 000 pixel heightmap. Both upscales were made using the original 3000 x 3000 pixel heightmap and slicing a 1:16th piece (or 1 km2 piece) from the bottom left corner of each of them, in order to highlight the difference in details. As you can see, the differences are most notable between the Original and the smaller upscale, but from there, the added detail is a tad harder to notice - but it is there. After this, I also tried to ramp up the data points, or in other words subdivide the plane even further and see how much that changed the outcome. The settings are identical,