GDR Forum Index
Podcast Podcast
Dev Dev Logs
Search Search
RSS RSS
Register Register
Log in Log in
Reply to topic GDR Forum Index -> Game Developer's Refuge -> Development Log - Glue Gun Glen Page Previous  1, 2, 3, 4, 5, 6  Next
View previous topic :: View next topic  
Author Message
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Sat Aug 12, 2017 2:52 pm    Post subject: Reply with quote

Working on adding various possible rules restrictions on rotation. I've added ability to disable/enable left/right rotation, as well as an option to specify a valid range -- maybe level allows you to only rotate from 0 degrees to 90 degrees, etc. I think it's working properly in the code, but all of these things -- especially things involving "I'm actually going to ignore your keyboard input, what do you think about that?" require clear indication that "i hear you, but you can't do that." Nothing is more clear than an in-your-face center-of-the-screen message banner, so that's what we're going with right now.



They can stack up to 3, I realized when spamming an invalid rotate that it was really annoying to have them stacking up the entire page. I have them set for 4s display time, then they fade out. Planning to code support for ESC to hide all warnings, or most recent warning, or all, I'm not sure. And probably some user settings where you can customize length they're shown, if they're shown at all, maybe where they're shown -- center, corner, new popup window, email notification, anything's possible. Anyway, these rotation rules will give me better control over the levels -- I decided in my last test level, it was a staircase, and it'd be cool to have a puzzle where you have to use the player tokens to help build a staircase to the top, but that puzzle dies fast if you're allowed to rotate the level however you'd like and you can just walk downhill to the "goal" area. Well not after the next update, not anymore then.

I also followed through on the "can't push other player token" unless holding control thing. I think that's going to be nice. Oh, and I threw a custom font into the game. I'm not completely sold on it, but it doesn't drive me crazy and I got tired of seeing Arial everywhere, or whatever generics I was running.

Thinking about adding idle animations to the active player token. Jump back and forth a little bit, do a little warble, I don't know. Hard reset upon keyboard input. Well, I also want to add some status labels to the screen somewhere that describe these new rotation limitations, so you can't have to wait until you see a nag message before realizing "damn, i didn't know i could only rotate 5 times in this level."
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Sun Aug 13, 2017 7:21 am    Post subject: Reply with quote

I've reached a new low.

View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Sun Aug 13, 2017 10:54 am    Post subject: Reply with quote

Apparently today turned into "make performance suck less in firefox" day. Now, I have the "luxury" of running a fairly antiquated Firefox 31.0. This piece of software is not on the level of my relatively up to date copy of Chrome, which had some side effects.

The main culprit of the performance suckage was the stars in the background of the game. I was rendering up to like, looks like I had 50 of them added a fixed position elements with a random rotation and opacity value for effect. Never noticed a problem in the least in Chrome, but "thanks Firefox" it apparently choked and died on the stars. So, for now the stars are disabled pending further review. And it sucks, because I liked the stars. Maybe i'll just prerender them on a background wallpaper too, or do a browser version check before rendering them.

I added a simple fps counter to the upper right corner for analysis. Then I added in "catch up" logic, where the logic loop is run however many times it needs in order to catch up to where it would be if running at 60fps. This system works well, on a logic level at least. (Nothing's going to look as good from an animation perspective when you're dropping 3 out of every 4 frames, though.)

The older firefoxes (not sure about the latest and greatest) also have some differences in key codes. Chrome was giving me a very tidy "ShiftLeft" and "ShiftRight" but "thanks Firefox" felt like just giving me "Shift" was enough. Thus, I sat there wondering, why isn't the level rotating? The good news is that "thanks Firefox" does have the decency to include a location property, 1 for left and 2 for right, so I can still figure it out in the end. I also changed the rotation animation logic to run on a timer rather than ... well, it was running on a kind of timer already. Whatever. it accelerates on a parabolic curve now, rather than a linear change.

I've experimented with queueing up elemen transform changes and only applying them after the entire game loop has completed all logic calculations. I had a bit of hope that "thanks Firefox" would immediately say "thanks Programmer" and jack the framerate up to 60, but my very limited experiment didn't pay off quite that nicely. The idea of queueing up the css changes has something to do with the time expense of modifying DOM elements, which leads to fresh calculations the next time you query for position / dimensions / whatnot. We'll see if I can get some more performance out of there at a later date and time, but for now "thanks Firefox" is running at an alright pace.

And, I expect many people are running later and greater versions of "thanks Firefox" than my paltry and embarrassing "you can upgrade software?" version, and will yield more impressive results. Still, I shall do what I can to support older browsers too, within reason. early access link
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Sun Aug 13, 2017 9:25 pm    Post subject: Reply with quote

Damnation. Work has begun on adding "carry object above me" logic, if you move to the right then the object above you also moves to the right. That part actually works partway (I have to add in recursion to carry entire stack), but in the process I've somehow broken some aspect of gravity, and falling from certain heights gets you stuck crooked in the ground, and then other objects can fall through you instead of landing on you. By the way, gems are in the game now. Collect the gems to win, sometimes. The collection animation is actually pretty cool, gem disappears into the center of the screen. But what would be cooler would be if this damned gravity worked right. And the color block match checking isn't working again, for the seventeenth time this weekend. It could be worse, I could be coding prerendered outline generator logic.

edit - got it
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Mon Aug 14, 2017 2:08 pm    Post subject: Reply with quote

I've found a pretty cool graphics set! This started when I was searching for a picture of spikes. (Yeah, I can draw spikes on my own, but someone else can draw them better.) I've been using a site called "flaticon.com" for some of the graphics to date, such as the crate, the gem, and some others. (I wish they had a damned colored block intended for puzzle games, because I still haven't come close to being happy with the colored match blocks.) Anyway, flaticon.com actually had very little in the way of spikes, which was a shame but somehow my followup searches for "free game spikes" led me to the unity website, where you can apparently buy various game arts, or you can find some free offerings as well.

Thus, various gears are trying to spin in order to start transitioning the graphics. It's not so much that there's a transition to make because I was only using like 3 tiles before, but it's more like "I never really coded any substantial tilesheet or level editor support" and now I've got to move forward a bit on that side of things. 1s and 0s worked great for a while, oh and also 3 for gravity zone and 5 for "empty section of level." Hmmmm.. It's so easy to just use 3 or 4 numbers and hack in the test levels via a text editor and a json file. More damnation.

Work will now begin on a tile picker for my level making needs. I probably could have already created one in the time it takes to post this, so obviously I'm stalling again. After that will likely be support for animated tiles, because I'm going to be adding lava to the game as well, oh! how interesting -- at first I thought "well, maybe lava and spikes are the same thing, maybe this is redundnant" but then I realized the obvious truth, it's that you can push blocks on top of spikes and they won't disappear but if you drop a crate in the lava, it's gonna sink to its fiery death. And maybe you needed that crate to reach some place, who knows?

I suddenly have this very terrible idea to create levels as components. Then, instead of the entire screen rotating when you hit shift, only one of its components will rotate. How this would interact with the recent rotation limit rules I added support for over the weekend, well that's a fair question; would each component have its own unique restrictions? (Just how much information overload can I throw at the player?) How would you control which component to rotate -- perhaps the one that contains an active player? Would it be fair to only permit rotation of components with an active player within them? It ... it's an interesting thought. "Now entering: central chamber" appears on the screen as your player enters the central chamber. Here are the rules for this location. Well, this is irresonsible and completely hypothetical speculation. I don't even have a tile picker yet for a single static level, and I still want that lava.

I fixed the color matching not working bug. Right, it was a scenario where I'd added the ability to compare objects only against other objects of a certain collision type -- I added this with the gems, so I could say "if player collides with non-colliding objects" i.e. the gem. Regrettably I did not properly code the "is optional parameter for collision matching behavior " flag checking right, and the function returned no result if the "optional" parameter was missing.

Much to do.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Mon Aug 14, 2017 9:48 pm    Post subject: Reply with quote

We're making a little bit of progress here. I got the tile picker in, including ability to pick "no tile" to make the irregularly shaped levels. The tile pack I downloaded actually had 2 tilesheets (a sequel, one of them I think), and I still need to merge most of the tiles into a single tilesheet. I've also got a json file that defines which tiles are colliding and which or not, an improvement over the previous logic of "if tile === 1 return true;" no doubt.



Nothing live yet -- I'm still doing a good job of breaking things during this transition. One of the things I've obviously managed to screw up is the prerenders, because the outline isn't correct again. I was previously doing a check against tile type 5 to find the edge of the levels, and -- because of my great foresight as a programmer -- I hard coded a lot of the calls to check for "tile 5". Well, tile 5 is actually a real tile now, and thus we're switching to -1 for empty tiles, but I could have missed a replace on that when trying to update the outline file. Furthermore the darned outline doesn't quite fit, because I changed the size and apparently there's some corresponding variable I have hidden in the logic somewhere that also needs changed. Hey, is anyone interested in maintaining a really unstable outline generating codebase for me? No?

The lava is just static tiles right now. There's a nice looking animation for it, but I just haven't gotten quite that far in the code yet. Hopefully soon.

The pack also came with some sprites, and I'm going to take a look at using the main character sprite to replace the ... slightly uncharismatic block with arrows that presently serves as player token.

The farther-out plan is to bake as many of hte tiles into the prerenders as possible, anything static. For right now it's all single tiles in single cells, and this is obviously gonig to remain functional even after the prerender udpates, because I have to see what I'm making in editing mode. The animations can't be part of the prerender. I have this idea for the spikes -- there are spikes in this tilesheet set, which is great because I have been looking for them -- I have a curious idea of animating them somewhere, whether by minor rotation or something a little different. Maybe.

Hmmm. These bridges, they look like their collision size is less than a full tile. I could get away with calling it a half tile high, but that's about it. I'm going to have to think about implementing some kind of variable collision checking, perhaps 1 is full tile, 2 is half tile, 3 is lava... I need that lava, man...

Ah yes. The editor did get a little additional attention, as I coded in a file fill logic. Not sure I have done this in an editor before, but I've done something quite similar in the hellish code that fills the prerenders. Or, doesn't fill them now, because I might throw that entirely out the window. ha ha ha! Drag paint also works, no sane individual could last 5 minutes clicking on every snigle tile manually. I don't even subject people in hell to that kind of torture, usually.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Tue Aug 15, 2017 6:41 am    Post subject: Reply with quote

Funny story. When I wrote the first version of the script that generates the vertex list that defines the outline of each level (and any other region that requires an outline), I started with a level tilemap like this:

Code:
0 0 0
0 0 0
0 0 0


Then, to work around some array bounds check trouble I was having, I had an idea of adding a "padding layer" at the bottom and right edges, leading to a temporary tilemap like this:

Code:
0 0 0 -1
0 0 0 -1
0 0 0 -1
-1 -1 -1 -1


I chose -1 because "-1 obviously isn't a real tile." And then however many days later, here I am using -1 to define empty areas of a level. Anyway, the story continues:

Because the script supports either "match this tile" behavior, where you generate an outline around every group of "tile type 3" -- and also supports "outline anything that "isn't this tile", e.g. to generate the level borders I say "outline anything that isn't -1" which is logical -- because of this dual behavior, I created a simple compare(a, b) function that would either check for equality or inequality, depending on the specified outline behavior.

But with the addition of the extra -1 column and row on the bottom / right edges, I needed a way to make sure I never outlined them for any reason. This led to this gem of a function definitions:

Code:
// comparison function, is it match tile type or not-match tile type?
function compare(invert, a, b) {

    if (!invert)
        return a === b;
    else
        return a !== b && a !== -1 && b !== -1;

}


which was working just fine until it turned out that -1 was actually a value I might want to compare against. Damnation, as usual. This is the culprit for the most recent "outlines don't generate properly" bug -- it's not that they weren't generating properly, it's that they simply weren't being regenrated at all because the vertex finder wasn't returning anything to outline in the first place.

I also solved the alignment issue. "Solved" actually, because this is what I was doing before, and it's what I'm doing now with slightly different hard-coded numbers. Remind me of this when I'm checking in next week talking about how "the outlines aren't aligning properly again, it's not my fault"

Code:
...
style("left", "" + (outline.x + 24) + "px")
style("top", "" + (outline.y + 24) + "px")
...
View user's profile Send private message Visit poster's website
Sirocco
Moderator

Joined: 19 Aug 2005
Posts: 9461
Location: Not Finland
PostPosted: Tue Aug 15, 2017 9:19 am    Post subject: Reply with quote

I've grown rather fond of an approach where I concentrate on centering data in an array that's much larger than I predict I'll need, rather than obeying my OCD and starting things off at 0,0. After a while it's nice to be able to cheat a little and put a "nope" border around your data :D without having to rewrite/shuffle a bunch of stuff around.

Dicking around in openGL kinda got me into that mode of thought... having 0,0,0 being the center of the world rather than the upper-left corner.
_________________
NoOP / Reyn Time -- The $ is screwing everyone these days. (0xDB)
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Tue Aug 15, 2017 11:46 am    Post subject: Reply with quote

Creeping forward on the animations front. Snakes without Brakes had a "resource controller" object that had a list of all of the graphics and sound files the game used, it preloaded them on page load, offered percentage downloaded, etc. I had been using the same object, but I've now committed a moderate refactor to it, which delays the progress on other things. I mostly concentrated on making the resources more generic, and also exporting the actual resource list to an external json file rather than hard-coding them into an array I had a temporary moment of confusion when I considered sound effect "groups", a concept I added to SwB where the "hit wall" sound effect actually pointed to 2 or 3 separate files, each with a slight variation. So the "hit wall" resource has 3 files, however the "lava" resource is only going to have one file -- the lava sprite. I decided to give every resource a list of files, even if for the graphics it's always* (I guess?) going to be just one file. (Well, if I ever get another bright idea and decide to have some variants on graphics too, i will be ready then.)

I've also prototyped a simple animation rules object and a corresponding definition file, I'll define the spritesheet source for an animation and then define the frames, along with the duration of each frame. Nothing too elaborate, so I think the object itself will perform fine, as long as I put the pieces in the right order now. Planning to start by adding a new property animations list to the level object, and corresponding data list in the level file. Parse the animations, place them in the right spot, and call object.animate(). Sounds pretty doable.

Then, once I have the lava, I can start programming how things die in lava. And by the way, before anyone gets any smart ideas and says, "what's going to happen to the lava when you spin the level upside down? it's going to drip down to a new location, right, and you'll have liquid physics supported?" No. the lava is just going to sit there on the ceiling, happy as a clam. And there's not a damned thing anyone can do about it.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Tue Aug 15, 2017 5:15 pm    Post subject: Reply with quote

I know you want to jump into the lava, because I want to jump into the lava. It's fun! Watch in horror as the lava's ebbs welcome you into its open, fiery arms with a smoky cough, and then see your helpless carcass sink slowly into the lava, only to ultimately expire in another last gasp of smoky helplessness. early access link
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Tue Aug 15, 2017 10:02 pm    Post subject: Reply with quote

In what is a very pleasant surprise, the creature animation logic I wrote appears to be working almost straight away. Will post a little more about it later, although it's nothing unusual. In the meantime, I have basic left/right walking animations for the new player creature in development.

View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Wed Aug 16, 2017 6:07 am    Post subject: Reply with quote

LIke I said, the format for my animations file is pretty straightforward. Here's a snippet of the creature animation list

Code:
[
    {
        "name": "player",
        "frameWidth": 44,
        "frameHeight": 44,
        "framesPerRow": 4,
        "behaviors": [
            {
                "name": "walkRight",
                "frames": [
                    {
                        "frameIndex": 0,
                        "minDuration": 20,
                        "maxDuration": 25
                    },
                    {
                        "frameIndex": 1,
                        "minDuration": 20,

                        ...

            },
            {
                "name": "walkLeft",
                "frames": [

                    {
                        "frameIndex": 8,
                        "minDuration": 20,

                        ...


The frameIndex property corresponds to the particular animation frame to render at the point, thus I can have multiple "frames" (I decided to call them 'steps" in the code to try to keep things clearer) that reuse the same spritesheet frame, such as might happen in a walking animation. The previous file format I threw together for the tile animations (e.g. lava) was much the same, but did not include any behaviors and assumed a linear frame progression at all times. It also didn't specify frames per row, just assuming a one-row spritesheet. That works fine for everything so far -- lava is 3 frames -- and I expect that it probably will work fine for anything I need, but it's possible at some point I'll decide to quickly port it over to use this updated format and simply give every tile a generic "default" behavior or some such thing.

Both animation lists (tiles and creatures) are now defined as resources in the resouce list file, and are loaded before the game launches the first level. Just before the game begins, once the resource manager reports that all assets have been loaded, the game parses through the creature animation file and creates animation definition objects, which for all intents and purposes are "constant" objects that store the rules for these things. From that point, each creature animation object has a method createInstance(behavior) or some such thing, which returns an object with current frame step, time until next frame, and spritesheet offsets so I know which frame to render for the animation. When a player token moves to the left or right, I (a) check the current animation behavior -- if the current behavioar is "walk right" and the player now moves to the left, I make another call to createInstance, but of course now I am requesting the "walk left" behavior. If the player already moved in that direction, then we skip first step and go to (b) where I pass the "animation instance" to the rules object. The rules object then processes the frame step, frame delay properties for the instance it received, and when it's time for the next frame it adjusts all of the instance's variables accordingly. If animation change occurs, it returns a true value -- this signals the player.move() function that an animation change has occurred, and the rendered graphic should now be modified. If no such change has occurred, it obviously receives false and saves time by not updating anything.

The main "bad thing" I'm going right now -- I rushed a little bit to try to get this working last night -- is there's currently no validation on the behaviors. i.e. I'm calling for animation data like this

Code:
this.animationInstance = level.resourceController.getObject("creatureAnimations")["player"].createInstance("walkRight");


It's fair to say that if "creatureAnimations" failed to load for something the game is screwed anyway, I will likely check for things like this before runtime (loading screen end) and ideally display a "sorry, come back later" or some kind of limited retry system where I reload the assets again. It gets a little dirtier when I ask for ["player"] directly without any kind of null checking. The same problem as before kind of applies here -- if there is no ["player"] property then the game is going to be glitchy looking already and the lazy part of me could also make an argument that these are my options:

Code:
// version 1
// bad array check throws a javascript error and stops / limits execution of the game
this.animationInstance = level.resourceController.getObject("creatureAnimations")["player"].createInstance("walkRight");

// version 2
// better validation, but what happens on failure?
var creatureAnimationData = level.resourceController.getObject("creatureAnimations").getCreature("player");
if (!creatureAnimationData) {
    console.log("Warning - cannot find creature animation data");
    // and now what?
}


And I guess the answer to "and now what" is that the player "ice skates" around the level, however because if I had this validation, the game itself would continue without a problem otherwise. And this means I should probably add it, even though the lazy part of me is still claming, "if the player animation data isn't there, then the ice skating is going to be stupid, and so your failure response should be to stop all game logic and force some kind of reload". It continues, "and by not having validation, the code is going to run a few nanoseconds faster. That's important!" He makes a very compelling argument. I can have slightly faster code, and it will require less work. The only tradeoff is slightly less resilient code. Hey, why does my outline generator code keep running into issues, anyway?
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Wed Aug 16, 2017 11:24 am    Post subject: Reply with quote

Cleaning up some of yesterday's minor messes. I've added a new emitSmoke() function I can now call instead of copy/pasting the same 12 lines of code in multiple places -- I really wanted smoke fast, I guess. I also followed through on the animation error checking, which turned out to be pretty simple after I added a setAnimationBehavior() method on the player object and just did the validation in there. Also added a couple of new animation behaviors, for "falling" and "drowning" at least. These are splendid additions. The death by lava animation is particularly nice, the poor guy really looks like he's losing the plot. And he is. What a shame.

I also did a little tools maintenance. I slapped together a tool that will take a spritesheet and generate mirror images of each frame, which I would use for taking a spritesheet of only "walking right" and make waling left, or perhaps take "lava" and generate all four directions, etc. I got a little overly aggressive for a moment, creating a player spritesheet for all four map orientations and left/right walking for each direction, until realizing I had "temporarily disabled" the getRotation() method for player objects, and all I have to do is rotate the player against the map to keep him upright at all times. Well, that and I have to use the "walk left" animation when the player walks to the right if the map is upside down (or rightside left, or something), but that's no trouble.

Another minor tool I put together walks through all files and folders in a given directory and finds all animation.json files, then merges them all into a single file as an array. This lets me have a single file master list of animations, while at the same time making maintenance of them easier because I can just open player.json, instead of searching through the master list.

I think porting tile aimations to use the new behavior-based animation is inevitable, because I want to have lava on the ceiling as an option. I guess that's probably next on the agenda, even though it won't be much trouble I'm blase about acting on it. Plenty of other options on the todo list if I want to weasel out of it.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Wed Aug 16, 2017 10:03 pm    Post subject: Reply with quote

I'm experimenting with adding a glue gun to the game. The idea is that you can cover one or more sides of a crate, etc. with glue, and then that side will stick to the floor / wall, and also stick to other crates / blocks that come into contact with that side. I have a very rudimentary beginning implementation in testing. so I can shoot a crate from the side and cover it with glue. But, the glue currently has absolutely zero effect (doesn't stick to anything), blocks certainly don't stick together if glued, and the entire thing -- including the shooting itself -- dies when the map is rotated, just like most other ideas early in development.



It won't be particularly easy to code. I'm not 100% on the idea, but I think it's interesting, and it would no doubt add another puzzle element to use in the game. More likely I keep it, because of that. Consider using glue on a block, then rotating the level so the block falls and glues to a wall, thus forming a new ledge you can use to get to a gem, or something like that.

Added a little bit more functionality to the animation system -- one animation behavior can now specify another behavior that follows it. In this case, the "shootRight" behavior lasts for some duration, and then indicates that following the shooting animation, the character should move to the "walkRight" animation. I should also be able to use this functionality to create idle animations and other such things.
View user's profile Send private message Visit poster's website
Sirocco
Moderator

Joined: 19 Aug 2005
Posts: 9461
Location: Not Finland
PostPosted: Thu Aug 17, 2017 5:46 am    Post subject: Reply with quote

Oh, wow. This went in a different direction than I was expecting. It's amazing to see what a re-skin can do for a concept :D
_________________
NoOP / Reyn Time -- The $ is screwing everyone these days. (0xDB)
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Thu Aug 17, 2017 6:45 am    Post subject: Reply with quote

Boring screenshot of my coding setup that nobody asked for. A little unnerved by how many files I keep open, once I open a tab I hate to close it. Sublime is a pretty decent editor in my experience. I don't use anything very fancy, but a couple of things it has going for it:

1. starts up very quickly, even if you have 1700 tabs open across 4 windows; saves session automatically. individual tabs may not load immediately if you really do have 1700 open, hard to get mad about it i guess
2. has some kind of regex-ish "open files" search, where you hit a keyboard shortcut and you can start typing the name of the file you want. i use this a lot, e.g. i want file Level.js, I hit the shortcut, type in 'lev' and generally it's like "hey, here's Level.js, you want it right?" and i hti enter and i'm there.



[damning lie]Maybe some day i'll register it?[/damning lie]

Quote:
Oh, wow. This went in a different direction than I was expecting. It's amazing to see what a re-skin can do for a concept :D


Can definitely admit some of this never crossed my mind until I noticed there was gfx for it. No complaints.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Thu Aug 17, 2017 11:22 am    Post subject: Reply with quote

A brief technical overview of how the glue rendering works.



It's clear to me that there shouldn't be a restriction on which side of an object the player can shoot. Because the map rotates, inevitably the player may access any side, and furthermore a block will roll end over end when you push it. Thus, what happens if you glue one side and then roll it over and shoot it again? It has to put glue on the next side.

To make this work, I first defined each side of an object with a ... bit value? I don't know, they're all multiples of 2, so top 1, right 2, bottom 4, left 8. Forget the technical term for this system, but you know what I mean. Thus, I can conclude that value 5 means "bottom glued and top glued" because it's 4 + 1. Using this logic allows me to create a reasonably simple spritesheet of the 16 various glue permutations. When the player shoots an object with a glue gun, the game first looks at which side of the object you hit, initially ignoring any additional rotation data. So, it's always going to be left or right here (you can't shoot upward or downward). But, let's say you've pushed the block one square to the right, and because it rolls end over and, you are now actually shooting the bottom of the crate (rolled once to the right by 90 degrees). So the 8 = left value must be shifted once down, or twice down if you've rolled it twice, and so on.

Animation of the glue application was another little challenge. I wanted an effect where the glue expands from the midpoint, like it's unfolding or something. Doing this for the initial glue gun shot seemed relatively straightforward, but I wasn't right away certain about "adding on," how to animate the second application of glue. I decided to keep it simple and add another div on top of the block, a simple temporary mask layer that animates the appropriate side, then removes itself once the glue animation for that side is complete -- afterward the permanent glue mask layer is updated to reflect the glue status of all sides. I expect that the temporary animation would continue to render properly if you were to push (i.e. start rolling) the crate again before the glue "dried," but I haven't tested that peculiar scenario yet. The animation is like 0.5s anyway, so it's not something that would happen often.

Here's another interesting strategy element: if you push a crate it rolls end over end. Maybe that's what you want, but if you glue one side and then roll it over too many times, the glue's going to touch the ground, and your crate / whatever is now stuck. (Forever?) However if you pull the crate (holding control moving away), it does not roll end over end. I'm sure I'll find a way to make use of these behaviors at some point.

With the rendering logic in basic working order, it's time to move on to the area boss. I have to make the glue actually interact (stick to) things. A little nervous.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Thu Aug 17, 2017 3:53 pm    Post subject: Reply with quote

The glue gun is ready*. Alright, so I haven't done anything in regards to block-on-block action -- you can't glue crates to each other, but objects now successfully glue to the walls. What I have for you now is an honest, real level, the kind of that could be part of the game itself, not just a whimsy test level. I've taken advantage of the new glue gun puzzle elements to create a level that absolutely demands it.

In full disclosure, there is not a win condition yet, and you will not receive a victory message after getting all of the gems. You are playing "honor policy" where you agree not to cheat, etc. Anyway, the glue gun so far is looking like a really cool add. It appears to work even when the map is rotated now, but if it doesn't then it won't surprise me either. Good luck to those who try. link to glue gun level
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Fri Aug 18, 2017 6:03 am    Post subject: Reply with quote

Oh, eternal damnation, thy name is performance and thou hast sitten aside the door for too long! Weep now, as the foul stench of your dropped frames permeates the illusory safety of the door you lurk outside, casting such a blight upon my domain that I can in no good conscience ignore. In plainer terms I'm exploring possibility of switching from html dom elements to using a canvas element. It's not that performance has reached any critical levels yet, but I do notice that the frames stop dropping when I put "too many" gems on a level and then begin with the rotations. Although the scene renders nicely at default rotation, I believe the combination of having to rotate the entire scene followed by having to apply a rotation transformation to each individual gem (beause they "dance" or "wobble") starts to take things just a little too far.

Before committing to at least considering this transition, I tried / thought of a couple other options. First I threw together some additional scriptage to draw all of the static tiles into the prerendered background. This was the plan all along, something I wanted to put off for longer because it's stupid programming task, but now was the time to do it. I thought perhaps the large number of td elements on the page (I rendered the game within table cells) might have taken some substantial processing on page rotation, but this didn't necessarily seem to be true -- didn't see much fps difference either way. Another, somewhat wilder idea I had involved manually rotating the tilemap of the level, along with the positions of all objects, to essentially create a new copy of the level but rotated in a given direction. This is an interesting idea... but probably ab it on the dangerous side as well, from a potential bug perspective and such. (Have fun coding in undo move functionality when you're doing all of this?)
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Fri Aug 18, 2017 12:10 pm    Post subject: Reply with quote

Stream of thoughts logged during work on a possible port process. Long story short, didn't have hardware acceleration enabled in chrome (tends to act buggy), without it canvas was slower than divs, afterward canvas hits 60fps but then with that same acceleration divs can also do 60fps, so... huh...

First step is to add some functionality to the resource controller, and I will also have to properly include graphic files in the resources list. Been "cheating" and getting away with background: url(player.png) in the css sheet for all this time. Not that there is another way to do that if using dom, but I wasn't including those in the resource list for preloading. Briefly considered writing a script to just loop through data/gfx and grab all the pngs, but decided against. I have odd names like crate3.png (I actually really like this one now), player.resized.png (because original asset is 16x16 and my tiles are 44x44 -- don't ask why, idk). Could change the filenames to logic ones, then just strip .png from them and use that as the key -- maybe that's not a bad idea, i don't know, if i change the graphics of something i'm going to have to maintain something somewhere so i'll just maintain it in the resources list and change the filepath

Need to add proper callback support to loading resources. Current version has a separate beforeGame() function that just polls for the resourceController.isReady() and when it's ready, the game starts. Now because each level has its own gfx (prerenders), I have to periodically obtain new resources. Suppose I could just do another polling function, but instead deciding to pass the load resources method a callback, which is then shared with every singel resource that is loaded. Each time a resource is loaded, it checks with parent to see if all resources are done, if so the callback is fired. Hmmm. Sure hope there isn't ever osme race condition whereby 2 images finish at ~same time and ... well, whatever, it'd just load the level twice. I'll add a isDone flag before the callback to be paranoid.

Nothing rendering on canvas yet, blank element exists on page. Time to test the new resources logic. First run is a failure, building a resources list based on level file's prerenders list, but callback not firing. Okay, I added the option to define only a single resource, so it is like this:

Code:
// old version, annoying verbose because i decided every
// resource would have a list of files, because sound effect groups
// will have variants
{
  "name":  "player",
  "files":  [
    {
      "src": "playersprite.png",
      "bytes": 2398239823
    }
  ]
}

// now parser supports this, and just does files.append(singleFile)
{
  "name":  "player",
  "file": {
    "src": "playersprite.png",
    "bytes": 2398239823
  }
}


Great idea, right? A little easier to type, read through. Too bad when I went to addFile I passed the entire parent object, and bugged out the whole thing. Whoops. Works now.

Up a ltitle late last night researching performance stuff, experimenting with things. Work a little early, decided I'd just go ahead and get a jump start on this port. Net was down. Damn. Luckily it was short term.

Currently aiming to get level staiic tile prerender on the canvas, not concerned about positioning, just starting at 0,0. I'm thinking in the end I'll try to center everything at center of canvas, i.e. center of level will be rendered there, but no rush. Time to add a canvas-based level render function. Objects may come next. Hey! There we have it. A little trouble with getting the key values consistent when adding and retrieving from the resource manager, but nothing more.

think it's time to center the level at center of canvas, before trying to add object rendering into the mix. dirty rectangles is also going to be a thing before anything is said and done, but i'm getting ahead of myself. centering time. Aha! Easy enough for starters.

Working on simple tile animations now, i.e. the lava. Basic rendering is almost there, but the animation is offset by about half a tile to the upper-left. Not sure why. Okay, forgot to account for the empty padding around each prerender. I add 20 pixel padding to each border before prerendering, so that the outer-edge outline doesn't run off the screen.

Ugh. I've been transposing the source/dest rectangle coords for drawImage in canvas. No wonder lava is rendering buggy. Fixed that. It might be dirty rect time, because right now I'm just rendering frame on top of existing canvas data, no clear and redraw ever happens. Made me think the animation was still wrong for a minute. Trying to think about how to implement the dirty rects, I'm not sure I've ever done it before. Mostly concerned about overlapping objects, i.e. redrawing the static tile background before rendering lava frame is easy, but if there was also another object in that region then I'm clobbering his rendering.

Perhaps the answer is to invalidate every rect with a potentially moving object (player, projectile, enemy, etc.) at start of frame and redraw it? Hmmmm.... Damn. Reading an article about this scenario where they're making a simple "game" with a stars in the background, a spaceship, and press space to shoot bullets. Hello world stuff, and they're saying create 3 canvas, one for the stars, one for ship, and one for the bullets. I'm not sure about all these canvases, though perhaps I can settle one 2 canvas elements, one for the level itself and another for the moving objects? Let's experiment...

that padding I put on the prerenders keeps throwing me off. Appears I have a successful dirty rect system going on with the lava. Next step is to add the gems in, the original performance culprits, and then I'm also going to have to re-add rendering or map rotation.

Grrrr. I'm on irc trying to counsel some advice. Here come the "there's LIBRARIES that do this" talk from some corners. Like I realllly need a library to handle drawImage() calls for me! Damnation! Main question right now this: if I have a separate sprites canvas, would it be better to simple clearRect(entireScreen) every frame, or to build a list of all moving objects' locations, clearRect(eachLocation)? Not sure what the overhead is on clearRect, if callign it repeatedly would make it end up less performant than the entire screen clear.

After some back and forth between the irc people, it seems their larger consensus is that using 2 canvas is a respectable idea, and that I should use a single clearRect on the entire region. Although the discussion continues to bounce around. I'm wasting a little time here. I think my 5 minutes of fame are over here, though, other people are starting to ask questions. Damn them.

Check this amazing commenting style out, nothing redundant here

Code:
// clear animation layer
Level.prototype.clearAnimationLayer = function() {


But I feel bad if I don't put any comment above a function. Now I'm wasting more time pitching in to help some more guy with his regex. He wasn't including the global flag, so only one character was getting replaced. This port is never going to get done at this rate.

initial results are not at all promising so far. I haven't even introduced rotation yet, and performance is actually worse using 2 canvas than it was using strictly divs. This is disappointing! First plan is to try to go back to one canvas, see what effect that could have. (My guess: not much.) And holy cow, this prerender padding is destroying me. Just trying to get the dirty rect to repaint the correct areas took an hour.

Removed second canvas, down to just one canvas, no gains. I immediately noticed that clearRect() on an entire surface killed performance by 25%, but clearRect on multiple individual rects isn't faring much better. Seems the last hope down this road is discarding the context save/restore calls I make on the ends of drawing operations. The first code samples I looked at used them, I saw discussion elsewhere that it could be a performance killer. Unsure until I try.

Nope, nothing much of a difference there. Maybe a couple of frames.

An important detail I've neglected to mention, or fully consider -- I disable hardware acceleration in chrome. Not because I'm a jerk, but the browser does some buggy things (e.g. i switch to another virtual desktop, browser remains on screen), so i just don't use it. It turns out hardware acceleration is a nice thing to have for a game. I toggled it on (for now), and the results are murky. I say this because enabling the hardware acceleration has two results: (1) canvas performance improves to 60fps, this is good; but also (2) original div solution performance improves to 60fps, even after rotating the level and adding 50 or 60 gems, all over the level. This yields the obvious question -- why bother any further work on porting to a canvas tag when divs are actually faster without hardware acceleration, and equally fast (because you can't get above 60fps with requestAnimationFrame I think) with it enabled?

Not sure about the hardware acceleration situation for my decrepit copy of firefox. Perhaps I should get around to updating it. I didn't notice a meaningful difference for either div version or canvas version in old firefox, they both ran a bit on the slow side.

Thus concludes for now the exploration of porting to canvas element. Looks like I can do better without it, if hardware acceleration is on (and browser is less than 5 years old probably). Not fully convinced yet of any route, though.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Fri Aug 18, 2017 5:35 pm    Post subject: Reply with quote

Updated to firefox 55.

Still no decisions. I've added a constnat CANVAS_MODE_ENABLED that lets me toggle back and forth between modes while I'm evaluating. The canvas mode is limited but will render an upright level with the gems, good enough for a rough performance comparison. On my machine, the upper bounds for the canvas element appears to be in the 800x600 range. Going with 1024x768 ... well, I take it back. I just change it to 1024 768 agani, and now I'm right around 60fps in firefox. Just increased the surplus gem count to 80 or so, and I'm still holding at 57-58 with canvas mode enabled in firefox. Disable canvas mode, raw divs and firefox is down to 30fps. This is what I first saw after upgrade, the slower performance.

But, here's an interesting thought. The biggest bottleneck right now, at least for firefox? The rotation of the gems. They move back and forth from -30 to 30 degrees. Disable that logic, and we're back to 60fps even in firefox, even with 80 gems or whatnot. This leads me to a tempting idea -- prerender a spritesheet of the gem at every possible rotation, 61 frames. Rotation effect without rotation rendering calculations. I may have to give this a try. Could be a strong optimization for the non-animating gem, which currently is th eonly object that has constant rotation. (Unsure of the likelihood of this changing, either.)
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Fri Aug 18, 2017 6:48 pm    Post subject: Reply with quote

Well, there's no doubt that it's a performance gain, using the all-possible-rotations spritesheet. It's not quite as smooth looking, because I'm rounding to integers, but you can't have everything. Still, I remain unsure about the path forwrad, canvas or no. Despite some of the tricks I'm employing, firefox still doesn't keep up as well with chrome once the number of gems gets a bit up there. Maybe the usage rate of firefox isn't even worth it anymore, I seem to recall their numbers are down because google's stealing all their downloads, or something like that. I'm still very much considering the canvas approach, after all is being said and not done. (ha ha ha.) One of the pluses there would be finer control over what is and isn't rendering -- I could make levels 5 screens long, and only render 1 screen at a time, easily by bounds check. This would be harder, perhaps verging on not possible with divs. (I know, I know, generally anything's possible but at what time and labor cost?) Also the clear increase in firefox performance when rendering on canvas, before even factoring in longer levels, etc.

Damn. Imagine if I could connect levels to one another in the overworld somehow. You finish a level, and a door opens to get to the next level. You walk a little ways and then enter that next level. Heh. By the way, I don't even have map rotation in working order in the canvas version. Maybe I should "walk" before I "run" for a little while. Hmmmmm. Oh dear. So, imagine this -- imagine if there are torches, and when the glue passes through them the glue catches fire and turns into a fireball. Oh my word. I wonder what that would be like............
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Fri Aug 18, 2017 10:10 pm    Post subject: Reply with quote

Production conclusion to a confusing day of coding. Final decision is made, canvas is the decision. Still working on catching canvas up to rendering parity that divs had.

What works: core level rendering, player crate and gem rendering (with rotation and collection), lava animation*
What partially works: smoke effects don't clean up after themselves (dirty rect logic missing), lava animation doesn't repaint itself every frame so objects sinking into it clobber/overwrite it
Doesn't work at all: glue gun projectile, glue application animation, glue mask after animation completes, glue on wall when glue side touches... basically everything glue related.

Currently running a 1024x768 canvas. Will probably consider some kind of fit-to-page logic, and certainly so in the case that user's screen display isn't large enough to fit that much in. Let it go larger? Not sure, that would probably depend on user's machine and whether they're one of those suckers running firefox 31 or something.

Canvas port makes rotating the level so smooth, though, it's a substantial improvement. Orders of magnitude better than athe previous implementation, although in fairness the old one worked on a setInterval timer which may have impacted the smoothness to some degree. (Or may not have, perceptibly.) Gem collection animation also looks pretty cool, notwithstanding the fact I don't think it works "right" ... idea was to send it to the center of the screen, and it looks like it doesn't go to quite the right place, but I'm not sure I care -- certainly not at this point, and it looks good as it is. Why make more work for myself when I have all this other remaining? :)

Lava animation was an interesting case, because it doesn't update its display every single frame. So, say the lava delays 10frames from one frame to the next, well there's no need to repaint any of that during the 10 frames because nothing's changing, it's not going to change position, etc. 10 frames pass, I repaint background (dirty area) and the new lava frame, wait few more frames, etc. However, when the level is rotating, the entire level is repainted every frame until the rotation concludes. This means that the lava animation disappears for 10 frame stretches, only to briefly flicker and then get painted over again. So, the solution was to force constant repaints of the lava during animation period. Also a final asterisk on that one, because the level performs one final repaint upon finishing rotation, and then turns off the "rotation in progress" flag. Means I had to add a "forceFinalRepaint" flag to the lava repaint checker, so it could say "oh so the rotation is complete -- well i know it did a final repaint when that happened, so i'd better repaint myself too, just this one time."

Still thinking about that fireball idea. Don't let me forget.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Sat Aug 19, 2017 7:26 am    Post subject: Reply with quote

Lava and its lazy animation continues to be a bit of a conundrum. Here's whats up: when a player or object falls into the lava, the lava needs to enter "always repaint" mode, because it's competing for draw space with an object in constant motion. To start to solve this I added an isAwake property to animations. When I detect that player has fallen into lava, I scan for all animations in the player's bounding box and force them to awake mode. This almost works. But the other part of the equation, is that the currently active player is scaled to 120% size. It's not much, but it's only visual, so the bounding box for collision remains the same -- which means that by the time the player officially enters the lava, the rendering has already been overwriting the lava animation for a small number of pixels. So, there's a little artifacting in this time period. I'd prefer to only do the "wake up all animations" logic upon impact, rather than constantly scanning for such things. I could do a secondary collision check based on the scaled rectnagle area (the dirty region that the player would occupy) -- that collision check is fairly lightweight, anad that would probably work alright. I could also simply disable the sleepy animation behavior of lava and let it render every frame. It's not like there's going to be lava everywhere. I'll leave this for later.

Here's another small rendering bug I have at the moment:



Dirty rect logic says that the background gets redrawn, but because the player character scales to 120%, they render just outside of the level bounds when on the border. The subsequent calls to refill the region are technically still working, but the png prerender has transparency outside of the bounds, so it doesn't get cleaned up.

Smoke and glue gun projectile animations are now in good working order. Yeah cool, the glue gun bullet even obeys the slight arc behavior, so the rendering on these is looking solid. I think the last job here for now, to catch up with render parity in canvas mode, is to get the glue application animations working again. Well, that's not quite true, as the player's "climb ledge" animation is not rendered either, so he stands around for a moment and then teleports into place. I was going to have to redo that animatin anyway, because although a block with arrows on it rotating around its upper corner made as much sense as anything else, this little guy doing the same thing just looks dumb. Perhaps I'll have him do a couple of flips on the way up, or something.
View user's profile Send private message Visit poster's website
Diablo
Contributor

Joined: 19 Nov 2015
Posts: 350
Location: :( :(
PostPosted: Sat Aug 19, 2017 4:05 pm    Post subject: Reply with quote



The torches are ready to go. I changed the flame's color from blue to orange. It doesn't light the glue projectile on fire yet, although I did assemble the spritesheet for the fireball in preparation.

There's a early version of a camera system in the game now. It only works at 0, 0 at the moment so it renders the level at the 0,0 of the screen, while still preserving rotation of the level around its center point and every such thing. Shouldn't be too much work to add in scrollTo functionality, there's probably another parabola in the future somewhere in there, though if it's a player moving like in a side scroller, the adjust will want to be instant.

Level editing works a bit on the canvas now as well. I have all of the relative mouse coords logic set up, can paint tiles, still using the dom tilepicker (no reason not to, it's still great), and i've got a border around the currently hovered tile so i know just where the tile goes. Certainly more remains to be done, such as copy hovered tile, wire up the fill tile function again -- these are 5 minute jobs probably -- and also strongly considering adding "brush size" so I can pick, say a 2x2 region of tiles and paint it wherever.

Finished up the glue application animation port, looks great. I rewrote level export to give me a textarea instead of using console.log (why? who knows) but it got even worse when I side tracked myself into writing a self-written json pretty print function because I didn't like default JSON.stringify, because:

Code:
// tile map is like this
[
  [1, 1, 2, 3, 1],
  [1, 1, 1, 1, 1]
]

// JSON.stringify returns this though, annoying to look at
[
  [
    1,
    1,
    2,
  ],
  [
    1,
    1,
    1
  ]


look how annoying that is. Two reasons it doesn't matter, first because it doesn't matter, and second because text editor beautifies json data on save, and it does it the way i already prefer. So I wasted 30 or 40 writing something that saves me 1 or 2 seconds here or there. Sometimes you got no choice, though. Damned JSON.stringify.

Yeah, editor tile painting ran into a small problem because of the dirty rect system. Dirty rect always says "okay I'll update that, i have the prerender right here." But once I paint a new tile type, that's what I want to see, but I obviously can't edit an on-disk image file prerender from javascript. Instead I set up a backup copy of the original tilemap, and the dirty rect render checks original versus current. If it finds that I've made changes via editor, it does an additional draw straight from the tilesheet source, using the correct tile. This works well.

I believe truly the last catchup for rendering parity in canvas is to render glue spots on the walls when a glued object touches them. Just have teo add a list of such locations to the... heh, the level object, I've already done that too, I just have to point it toward canvas render instead of dom. After that, not sure. Scrolling perhaps, still have that climbing animation fix needing done. Glue catching on fire, that's one I'm highly keen for.
View user's profile Send private message Visit poster's website
Reply to topic GDR Forum Index -> Game Developer's Refuge -> Development Log - Glue Gun Glen Page Previous  1, 2, 3, 4, 5, 6  Next

Use this link to get a Sign-On Bonus when you get started!

All trademarks and copyrights on this page are owned by their respective owners. All comments owned by their respective posters.
phpBB code © 2001, 2005 phpBB Group. Other message board code © Kevin Reems.