grelf

Members
  • Content Count

    10
  • Joined

  • Last visited

  • Days Won

    2

grelf last won the day on February 16

grelf had the most liked content!

About grelf

  • Rank
    Member

Contact Methods

  • Website URL
    https://www.grelf.net

Profile Information

  • Location
    NE England
  1. Matt, It's a shame no-one has yet replied to this because it is not a totally uninteresting question. (For beginners: There is clearly a distinction between 'entities' which are objects in a game and 'objects' which are particular kinds of things in JavaScript (and other languages). A game entity is very likely to be represented by a JavaScript object but we need to be clear about the difference.) The question is whether each entity should have a record of its coordinates (generally x, y, z in 3D space rather than a tile number) or instead each space position (or tile on a 2D ground) should have a list of entities which are currently at that position. As you suggest, there is a difference between fixed entities and mobile ones. Fixed ones can clearly be referenced directly from tiles so that they can be drawn whenever the relevant tile is drawn. Difficulties arise when the mobile entities move. Should we scan through all positions (tiles) to find entities to move or, given a moveable entity how do we find the tile at its position? It seems to me that the most natural way is for each entity to keep a record of its position. But then the question is, for any given space/ground position is there an entity here? The way I do it in The Forest (www.myforest.uk) is indeed for a moveable entity (represented by an object of type Mover) to have properties x and y for its position. Then when generating the terrain within method Terrain.terra (x, y) there is a test to see whether this.placed [x + ',' + y] is undefined. If not, that object property (which looks like an array element but is not) contains a reference to an entity at that position. This relies on the optional way of representing object properties in JavaScript, where the name of the property looks like an array index of type String. Notice how I build the index string in the most compact but unambiguous way possible (x + y would be ambiguous: many possible value of x and y could give the same sum). The lookup is really done as a hash table, so it is very efficient. When one of my entities moves from (x1, y1) to (x2, y2) the previous property of the object this.placed is deleted and a new one is created and given a reference to the entity: delete this.placed [x1 + ',' + y1]; this.placed [x2 + ',' + y2] = entity; I find this works very efficiently within my (effectively infinite) terrain. I have a few objects that move every time the user does something. (For beginners: see https://www.grelf.net/jscourse/hashes.html and pages around there for more about ways of writing object properties and https://www.grelf.net/jscourse/moreops.html for the use of the keyword delete.)
  2. I have managed to rewrite my scene drawing (in www. myforest.uk) so that it does not reallocate two huge arrays and fill them with freshly created objects every time. This rewriting reduces the scene drawing time by about 30%. There is also less variability in the time taken from one scene to another which tends to confirm that garbage collection and reallocation were taking a lot of the time. Reallocation would be variable because it depends so much on what contiguous chunks of memory are available. I have written the following to show some things that developers may need to consider in their own code. My terrain is generated by a function of (x, y) position (in fact a method of an object of type Terra). It is a complicated function so it does not want to be done repeatedly for each position; instead its results must be held in arrays for subsequent reference during scene building. Two big arrays hold the results of that around the (moving) observer's current position to enable the scene to be drawn. For each position the arrays refer to an object of type ScenePoint which contains the distance and bearing of the point from the observer and other things (such as terrain details and amount of fogging for distant points). Each ScenePoint object has 10 properties so requires at least 80 bytes, probably more like 100 bytes allowing for the Object structure itself (I think that varies from browser to browser). One of the big arrays, around [x][y], goes out to the current view range which is user-selectable and can be up to 400 meters, so the array may have to be 801 x 801 to surround the observer. This is now allocated at the start of the program. Each array value will be a reference to a ScenePoint object so the array itself takes 8 x 801 x 801 = 5.1 megabytes. Then the 801 x 801 ScenePoint objects, also now allocated once at the start instead of freshly in each call to Scene.draw (), occupy about 100 x 801 x 801 = 64 megabytes. Another array, ahead , contains references to the same ScenePoint objects but is sorted during scene drawing so that the most distant points come first. This array does not contain references to objects behind the observer and so it is allocated as new Array (400 x 801). It requires a mere 8 x 400 x 801 = 2.6 megabytes. So my latest version of The Forest, v19.4.10 (see www.myforest.uk ) allocates just over 70 Mbytes when it starts and then no longer has to reallocate this (in many pieces) every time a scene is drawn. I still find it remarkable that my scene drawing is done in a couple of seconds even for the 400 metre horizon range. During that time the 'ahead' array is sorted too! When starting to draw a scene 'ahead' has to have all its elements set to undefined because as the observer moves and turns a varying number of points can lie ahead. I rely on the specification for Array.sort() which says that undefined elements get sorted to the end of an array. To support all this I have written a new JavaScript file to create one object of type Around. People may be interested to see it (below) but first Matt: Having made this change it was then easy to experiment with making 'around' a 1D array instead of 2D, calculating the index from x and y myself. I found no difference in performance (in FireFox). // Part of The Forest www.grelf.net (www.myforest.uk) // Copyright (c) Graham Relf, UK, 2019 /** One object of this type is constructed at the start to avoid reallocating * big arrays every time a scene is drawn */ function Around () { var el = document.getElementById ("range"); this.aMid = 0; // The middle index of each x/y array // Find largest range user may select: for (var i = 0; i < el.options.length; i++) { var r = parseInt (el.options .value); if (r > this.aMid) this.aMid = r; } var wd = 2 * this.aMid + 1; this.aheadChange (this.aMid); this.around = new Array (wd); for (var x = 0; x < wd; x++) { this.around [x] = new Array (wd); for (var y = 0; y < wd; y++) { this.around [x][y] = new ScenePoint (0, 0, 0, 0, 0); } } } /** Use at the start of drawing a new scene * NB: mex, mey are rounded observer coordinates */ Around.prototype.init = function (mex, mey) { this.xOffset = this.aMid - mex; this.yOffset = this.aMid - mey; // Clear previous scene: for (var i = 0; i < this.nAhead; i++) this.ahead = undefined; this.nAhead = 0; }; /** Only used if user changes the range (and in Around constructor) */ Around.prototype.aheadChange = function (range) { this.ahead = new Array (2 * range * range); this.nAhead = 0; }; /** Add a ScenePoint object reference to the scene ahead */ Around.prototype.aheadPush = function (sp) { this.ahead [this.nAhead] = sp; this.nAhead++; }; /** How many active ScenePoint objects are currently ahead */ Around.prototype.aheadLength = function () { return this.nAhead; }; /** Get a reference to the ith ScenePoint object ahead */ Around.prototype.aheadGet = function (i) { return this.ahead ; }; /** Sort the ahead array in descending order of distance */ Around.prototype.aheadSort = function () { this.ahead.sort (ScenePoint.prototype.sort); }; /** Get a reference to the ScenePoint object at (x, y). * This is so that extra properties can be added to it. */ Around.prototype.aroundGet = function (x, y) { return this.around [x + this.xOffset][y + this.yOffset]; }; /** Set the fields of the ScenePoint at (x, y) as if freshly constructed */ Around.prototype.aroundSet = function (distance, bearing, x, y, odd) { var sp = this.around[x + this.xOffset][y + this.yOffset]; sp.fogNo = 0; sp.tr = undefined; sp.building = undefined; sp.drawn = undefined; sp.clear = undefined; sp.d = distance; sp.b = bearing; sp.x = x; sp.y = y; sp.o = odd; }; Note that in my previous version of Scene.draw () each ScenePoint object was freshly constructed and subsequently might or might not get extra properties added (such as .tr or .drawn). That was poor practice really. For easier maintenance it should be clear in the constructor as to what properties an object can have.
  3. A method of generating dungeons can be seen here: https://grelf.net/forestdesign.html#N657813 except that I call them mines. A very small portion of the map generated by that can be seen below, in which the red squares are mineshafts coming down from the ground above (positions generated as part of a more complicated map, described elsewhere in the document linked above). You will note that the code shows that the map will be symmetrical about the line x = y but a slightly more complicated formula could change that. I use digits of PI shifted up to get more randomness. I only have one level of mines but obviously a third coordinate, z, could be included in the formula to get more levels. I first used this kind of method in the early 1980s and I have put copies of magazine articles about it on my history page: https://grelf.net/foresthistory.html#N656637 To see my code in action go to https://www.myforest.uk?x=15475.55&y=6085.52&b=100 , go to the scene (key s) and move forward (up arrow).
  4. Matt: you prompted me to reexamine my scene drawing code with a view to possibly improving its performance. It is even more surprising that I get the performance that I do, because the following is the gist of what happens with an array called "about" which is created every time a scene is drawn. Its purpose is to hold information gathered about the ground immediately around the observer (object called "me") out to a distance which is a user-alterable range (up to 400 metres, 1 ground unit per metre). var mex = Math.round (me.x), mey = Math.round (me.y); // Fractional array indices would not work var xyLen = 2 * this.range_m + 1; // max 801 this.around = new Array (xyLen); // "this" is the scene object, we are in its draw() method for (var xScan = mex - this.range_m; xScan <= mex + this.range_m; xScan++) { this.around [xScan] = new Array (xyLen); // ... start filling this.around by scanning both x & y around me My point is that this.around [xScan][yScan] is being indexed by ground coordinates which can be huge. I recently discovered that the width and height of my map are each about 1.8E15 metres (it could not be a planet in our universe). The code fails beyond that range but performance does not fall off before that! So I get away with it because of JavaScript's very flexible way of indexing arrays (and evidently very efficient in current browser implementations). If I were instead to allocate (the largest possible version of) the array just once when the scene object is constructed I would have to calculate indices in my own code so that they range from 0 upwards. I expect performance could easily be worse then.
  5. My approach is to use photos to avoid any complicated rendering, and use the basic "2d" canvas context without any third party libraries or frameworks. I do not use WebGL because I believe it is not available on all devices, whereas plain HTML5/JavaScript is. I want my efforts to work on everything from desktop to smartphone. I have been amazed by how fast images can be scaled and drawn in the 2d context. My prime example is at www.myforest.uk where the scenes are drawn in less than a second on current machines and yet each comprises many thousands of scaled copies of a few photos (sample scene below). The scenes are 3D in the sense that true perspective is calculated for distance from the observer but each drawn image is of course 2D. I use my own photos to avoid copyright issues. I edit them in Photoshop to cut out the objects I want against a transparent background and the PNG file format preserves the transparency so objects can be drawn sensibly in front of each other. I have the impression that the frameworks are each written for very specific types of games. I want to encourage people to break out from those moulds and be more creative. I know that my forest is miles away from mainstream video games but I hope it demonstrates that the basic HTML5/JavaScript platform can be used for very varied effects. In an attempt to help others I am documenting my forest, including how it is programmed, starting here: www.grelf.net/ojsvg.html
  6. I think there are two main issues: - Maintainability: using multiple indices, as in map [x][y] will be much easier to comprehend if you come back to the program years later to correct or enhance it. - Performance. There is no doubt that allocating a single large array is faster than allocating hundreds or perhaps thousands of smaller ones, which is what happens in the 2-index case*. Likewise the garbage collector has much more work to do in the latter case but if your arrays remain permanently through your program (do not go out of scope) that is not a consideration. In drawing the scenes in my forest I do allocate and release a 2D array every time (up to 400 x 400 so it can comprise up to 401 1D arrays). The time taken to redraw the same scene in my program is very variable and think this is probably due to garbage collection. I have been aware from the outset that performance might be improved by redesigning this but it is not my highest priority. * According to the JavaScript standard because there is no way to say new Array (m, n) to indicate 2D (instead you would get the 1D array [m, n] in this case).
  7. One example I used in my JavaScript/HTML5 programming course ( https://www.grelf.net/jscourse/ ) was a rather crude drawing program, grDraw. An exercise at the end of the course suggests some improvements to make. My own improved version can be found at https://www.grelf.net/grDraw2/grDraw.html I mention it because it includes some things that may be useful to other developers such as - a colour palette object which may help beginners to understand CSS colours - browsing to display a user's images and get spot colours from them - saving images in local storage and retrieving them (not as difficult as I first imagined) An example display can be seen below - echoes of Kandinsky?
  8. I have completed writing my free JavaScript course (begun in 2011). It is in two main parts. The first is aimed at complete beginners to introduce the whole idea of programming. It shows how to get some simple examples working and introduces some HTML5 as needed to make a platform for running the examples. It covers some general programming concepts such as writing requirements and testing against them. Part 2 is a more systematic exposition of the language, up to some of the more recent capabilities such as local storage and image processing. It is available at https://www.grelf.net/jscourse/index.html That link is to a contents page so that those who already know some of it can jump in to any particular topic of interest.
  9. As some have already pointed out in this forum there is a problem with scenes stretching ahead with a sudden cut-off at a horizon. Objects just beyond the horizon suddenly pop completely into view on the slightest forward movement. I have addressed this in my program The Forest ( https://www.myforest.uk ) by making the scene hazy (or foggy) towards the horizon, if the user switches this effect on via a check box on the page. A couple of example scenes are attached here. I have written a detailed description of how I programmed it ( https://www.grelf.net/forestdesign.html#fog ). My code is plain HTML5/JavaScript using no framework and only the standard 2D graphics context because I want my program to run on as many platforms as possible. So my documentation explains how to process images in that environment and indeed how to copy an object of type Image complete with its pixel data, which is not entirely obvious or straightforward. I hope this may be useful to others.
  10. New free game: The Forest See https://www.myforest.uk and documentation at https://www.grelf.net/ojsvg.html This is a redevelopment in HTML5/JavaScript combining two much earlier programs, "The Forest" and "Explorer", which I wrote and had published in the 1980s for the TRS-80, Sinclair Spectrum and BBC Micro. It is partly a simulation of the sport of orienteering and an aid to interpreting contour maps but there are several diversions for non-orienteers, including a challenging treasure hunt. It is set in a vast forest, effectively infinite. This rewrite demonstrates how powerful the HTML5/JavaScript platform can be. Detailed maps and complicated scenes are displayed in fractions of a second. The program should run on any device that has a browser, from desktop PC to smartphone; it works on my cheap Android phone even in battery-saver mode. A friend tells me it works on her Kindle tablet. Older devices may not be fast enough though. Despite the huge extent of the terrain, the game downloads and runs in seconds because the program is only about 120 kilobytes (yes, kilo!). And there is nothing to install. I do not use any third party libraries. I deliberately use only the basic 2D graphics context because others, such as WebGL, are not available on all devices. Please feed back to me (gr at grelf dot net) details of any device that you find it doesn't work on. Tell me make and model, and particularly the operating system and browser (with version), and what doesn't work. The program and its source are freely available, uncompressed, because I want to encourage others to program creatively on the HTML5/JavaScript platform. I am writing on my personal web site about how it works. Lots of background information can be found at https://www.grelf.net/ojsvg.html and other pages linked from there, including my free JavaScript course. I think HTML5/JavaScript is a good combination for rewriting retro games and making them available to all, rather than emulating the cumbersome old machines in software or physically reproducing them, as some are doing. Reprogramming is more work of course but it can be very satisfying (and JavaScript is much easier to write than assembler). The technology now makes it possible to write what I really had in mind back in the 80s.