• Content count

  • Joined

  • Last visited

About timetocode

  • Rank
    Advanced Member

Contact Methods

  • Website URL
  • Twitter

Profile Information

  • Gender
    Not Telling
  • Location
    Seattle, WA
  • Interests
    game development, pixel art, procedural content generation, game music

Recent Profile Visitors

923 profile views
  1. timetocode

    Pixel perfect click

    I think I'm going to stick with the extract.pixels technique, it is working nicely. With the broadphase and the caching of pixel data the performance is extremely fast.
  2. timetocode

    Pixel perfect click

    I tried both ways. I couldn't get the bezier curves to work fully... it was easy enough to draw them around the pieces by translating the context, but then that requires translating the context again for the collision checks which became a bit of a mess. Also Path2Ds aren't real javascript objects with clear cut properties.. they seem to have automagic properties that are part of the dom or the context or something else that is not conventionally accessible. Here's the pixel approach via pixi extract (v4). let pixels = renderer.plugins.extract.pixels(someSprite) That got the pixels from a sprite, which I cached on every sprite. Then here is the pixel perfect collision check including a broadphase: // x, y are the mouse coordinates coordinated to world space pieces.forEach(piece => { // broad phase check, where the collider is an SAT Box that fully contains the sprite if (SAT.pointInPolygon(new SAT.Vector(x, y), piece.collider)) { // how far from the top left corner of the sprite we clicked let px = Math.floor(x - piece.x) let py = Math.floor(y - piece.y) // use the sprite.width here let width = piece.width // convert 2D x,y to 1D index let index = px + py * width // get the alpha channel of the pixel (*4 because RGBA) let alpha = piece.pixels[index * 4] // if not fully transparent, then this object is under the mouse if (alpha !== 0) { piecesAtPosition.push(piece) } } }) // skipped: logic to choose which of the pieces is closest to the mouse if // multiple are under the pointer Haven't tried rotation yet, as its not part of the launch featureset. Presumably rotating the sprite, creating a render texture, and then overwriting the pixels array with the pixels from that render texture would enable pixel perfect collisions again. I'd like to note that while I'm using SAT for collisions (this is a multiplayer game and I have other collisions that are server-only) most people would do fine with just pixi collisions for the broad phase. It would be somewhat easy to add this to all of pixi in general.. though its really just for pixel-perfect picking, not for pixel-perfect collisions. Not sure if people would be interested or not. Rotation would be the hard part. Zooming/panning turned out to be fine with this method so long as the mouse coordinates are converted to world coordinates. For me this turned out to be a lot of math b/c the zoom is implemented by scaling the stage, pan by moving the stage, and then there is a "low graphics" mode that locks the canvas to a low resolution and then fills the screen via css. All this stuff needed applied to the regular mouse x,y to convert it to a position in the game world. Attached is a gif show clicking through the gaps in one jigsaw piece and grabbing the piece behind it, also clicking in the hole of one of the pieces and missing it.
  3. timetocode

    Pixel perfect click

    Anyone have recommendations for getting a pixel perfect hit (click) detection for some oddly shaped jigsaw puzzle pieces? I've already got non-pixi collision detection via SAT.js rectangles, but as jigsaw pieces are pretty irregular I'd like to use the SAT Box as a broadphase and then do a pixel perfect check. I have a few ideas but I'm not sure if they are overkill. One idea, since the jigsaw pieces are cut with bezier curves, is to export the bezier data along with the jigsaw puzzle spritesheets and then recreate the paths in a regular html canvas. Canvas has a isPointInPath function that sounds like it can tell if the mouse is in the bounds of the puzzle piece's bezier path... tho I've never used this on a complex multi-curve object so I'm not certain. I also have quite a bit of pixi-level scaling and zooming going on, so there would be much math. There's no rotation yet (and there might not ever be), but I guess these would hypothetically be reasonable to rotate. It also sounds like the path would need remade frequently, because in this game it is possible to pan/zoom and while the pixi sprites will be fine, these canvas-bound paths won't be coming along for the ride... they'll pretty much need to be re-pathed near where the player clicks if the view has changed at all. A more pixi-centric idea is to use the SAT Box as a broadphase, and then convert the relative position of the mouse over the jigsaw piece to the coordinate of an exact pixel within the pixi sprite. So for example if the mouse was at 50,50, and the sprite was at 45,45 and was 20x20 pixels big, then some amount of math can deal with the offsets and convert the 2d coordinate to a 1d index and tell which pixel of the sprite is under the mouse.... and then check the alpha...? I'm guessing I should cache the pixel data per sprite. How do I do this in a way that works both with the webgl and canvas renderers? Are there other good ways? Just having a non-canvas bezier isPointInPath would fix everything too, if anyone happens to know a lib that does that. Thanks
  4. timetocode

    Streaming models for large environment

    An environment can be broken into tiles or chunks. I've only done this in two dimensions, but given that a realistic map exists almost entirely along the horizontal plane I think the same logic from a 2D game can apply. In 2D games it is common to have tiles, and then we can convert the player's x,y coordinates (x,z if in 3d) to find the tile coordinates. The tiles are just in a 1D or 2D array. Usually its a 1D array because javascript arrays are 1D naturally and some simple math can turn x,y into an index within the array. Some more math can then load the tiles that are +/- 100 units away from the player (or whatever, depending on the view). This approach can work for fairly large maps, but at some point the maps are so big that the idea of having the whole map in a single array in memory won't work. When these maps get truly massive, we can no longer just have them in memory as an array... instead we need something conceptually similar to pagination, where we only work with a finite section of data that comes from something much larger or even infinite. In games this is sometimes called chunking. Minecraft popularized this term. A very large tile map (just an example) can be divided up into chunks that are 32x32 tiles. Converting the player's coordinates to chunk coordinates is just a matter of dividing their x,y by the size of a chunk (e.g. 32 tiles x 16 pixels, or 0.25 km). After we have the chunk coordinates we can load or generate the chunk the player is in, as well as any neighboring chunks up until we feel we have enough map to satisfy the view distance. In 3D this is going to be a similar process, but perhaps there are some cooler things at our disposal such as LoD. I don't know enough about babylon to guess at chunk sizes or what the general constraints would be.
  5. timetocode

    Keep child at original scale

    I'm not sure if this applies for what you're doing, but I've done player.scale.set(3, 3) let item = new PIXI.Sprite.fromFrame('sword.png') player.addChild(item) player.itemInHand = item player.itemInHand.scale.set(1/3, 1/3) This example is pretending that I've scaled the player artwork up 3x, and then added something called `itemInHand` to the player which I didn't want scaled up, perhaps because I had already drawn the object large enough. So the object is scaled up, but one its children is scaled down, just to undo the scaling. The general pattern is parent.scale.x = n; child.scale.x = 1/n; not that I've ever applied such a thing generally, only ever explicitly in corner cases. I'm also using a centered anchor (obj.anchor.set(0.5, 0.5)).
  6. To bundle sprites I use the free version of texture packer. The primary feature for me there is that I can just put all of the sprites, as individual frames, into a folder and point texture packer to it. Texture packer can then automagically produce two files: a spritesheet.png where all the images exist together, and a spritesheet.json that basically describes the locations (x,y,width,height) of each image. Pixi can load this and then exposes them via the function `fromFrame('/goblin_north_attack0.png')` etc. So PIXI.Sprite.fromFrame('filename') and PIXI.Texture.fromFrame('filename') are two ways to use it. On a technical level these are no longer the files that they're named after. These are just rectangular selections from the generated spritesheet. The folders become part of the sprite names, so one way to organize instead of goblin_north_attack0, is to make /goblin/north/attack/0.png and then that whole string ` /goblin/north/attack/0.png` is the name of the frame. I'm not super keen on naming something 0.png, but this can be nice if the source art work has a lot of sprites... and really it can be named anything, all we truly need from it is that it has a number somewhere that we plan on incrementing for moving through the animation. Multiple spritesheets are still an option though, even doing the above. One just has to be sure not to name a sprite the same thing in both sheets (easily done via the folder trick, as it ends up being a prefix to everything in it). I usually end up with a spritesheet for all the characters, items, creatures etc, and then another spritesheet for the game world if I'm using TiledMapEditor which has its own format. I've written a barebones exporter from TiledMapEditor to pixi if anyone wants it. I'd say that this solves many problems, and it does, but it isn't without some tedious work b/c usually whatever drawing program I use or whatever art I purchase has its own ideas about the frame format. For example most purchased sprites are sold as images containing frames in rows or grids. Most of the drawing programs I've used export animations as a row of sprites all right next to each other, or as a folder of individual frames that have been named automatically. They need to be cut up into individual frames and renamed to be put into a single sheet. If the volume of the work for cutting the images out and naming them is going to exceed an hour, I usually go fire up some imagemagick tutorials. Imagemagick is a command line script that can do things like split a grid-aligned spritesheet into multiple images. I end up doing this pretty much any time I purchase artwork... usually after a fair amount of opening the spritesheet in drawing programs and displaying grids to figure out the frame width and height, and sometimes some manual removal of areas that I don't plan on feeding into the scripts. I'd also like to note that the code I pasted above is very pseudo. Just skimming it again I can see that the checks for varying things are off by 1. There's also usually some section about facing left or facing right, and then taking the set of animations (e.g. goblin_right_attack) and flipping them by setting the sprite's scale.x to be -1 (only applies if left/right are just going to be mirrors of each other). Good luck!
  7. I'm not sure if this is improper, but I've always just skipped the whole animatedSprite thing and just made animations manually. I also just put every single sprite together in one spritesheet and name them stuff like goblin_left_run0, globin_up_attack6, etc. I put the whole game in a requestAnimationFrame loop, and invoke update on every entity, and then its up to that entity's own animation code to see if its graphics should change. Here's some pseudo code for the core loop that changes the frames, as well as idle, run, and attack. This would hypothetically be inside of a class, but I've written it just plain. // defaulting to the 4 frame idle animation let animationName = 'idle' let frameNumber = 0 let acc = 0 // a variable that stores time let maxFrame = 4 let frameDelay = 0.5 update(delta) { // accumulate time acc += delta // is it time for next frame? if (acc > frameDelay) { // next frame frameNumber++ if (frameNumber > maxFrame) { // loop to start of the animation frameNumber = 0 } // change the graphics sprite.texture = PIXI.Texture.fromFrame(animationName + '_' + frameNumber + '.png') } } // change to the run animation run() { animationName = 'run' acc = 0 frameNumber = 0 maxFrame = 8 frameDelay = 0.250 } // change to the attack animation attack() { animationName = 'attack' acc = 0 frameNumber = 0 maxFrame = 12 frameDelay = 0.180 } //etc Maybe in the end its not too different than the animatedSprite... except for very explicit control over the timings/loops, and no specific arrays of frames (though they're implied by the names of the frames in the spritesheet). Performance will be fine as in the end all any of this does is display a small subsection of a texture that is already loaded -- one can easily have hundreds on a low end machine, and sometimes many thousands.
  8. What's the right way to load multiple models from blender (or anything), and then spawn them many times into the game world? Let's say I have a tree.obj and a house.obj... and then I want to load it, without rendering anything yet, and then I'm going to programmatically generate terrain and add 200 trees and 20 houses. I've tried LoadAssetContainer, but I couldn't seem to invoke scene.createMesh or createMaterial on the data... all I could get to work was container.addAllToScene(). Also is there a friendly name for the loaded model? I was able to position it by doing container.meshes[0].position.y = 30, but I assume there's another way to interact with it. So the questions are: which file format for the blender objects (obj, babylon.. then one model per file..?) which loader to use how to load without spawning the object into the world how to spawn multiple of the object into the world (clone..? then position+rotate?) how to remove individual objects without unloading the source mesh Thanks for the help
  9. timetocode

    Loading models in NullEngine

    Looks like the answer is that './' and '/' are not valid paths for either babylon or xhr (not sure which). This syntax will work in the client, but not on the server. I was able to get it working with the full path: BABYLON.SceneLoader.LoadAssetContainer('http://localhost:8080/', 'cubio.obj', scene, (container) => { //etc...
  10. How does one load a *.obj in NullEngine? const BABYLON = require('babylonjs') require('babylonjs-loaders') // mutates something globally global.XMLHttpRequest = require('xhr2').XMLHttpRequest ... BABYLON.SceneLoader.LoadAssetContainer("./", "cubio.obj", scene, function (container) { console.log('CONTAINER', container) container.addAllToScene() }) Error: ...\node_modules\xhr2\lib\xhr2.js:206 throw new NetworkError("Unsupported protocol " + this._url.protocol); Do I need to do something to xhr2 to teach it about obj?
  11. I've got collisions versus a heightmap via BABYLON.GroundMesh. I've got collisions between my players and a few random walls and other primitives that can be spawned in the game via moveWithCollisions. I'm using NullEngine and everything is multiplayer, so the scaling is important. Anything that gets added to the world needs to be able to be part of collisions against a couple hundred entities each frame. The heightmap is super high performance intrinsically and has no problem. moveWithCollisions is okay too so far, but I only have a few objects in the game total... but I'm imagining that if this ever becomes too expensive I can use the octree in babylon or roll my own spatial structure. Now I'd like to begin putting structures into this world that the player can walk through. These houses are going to have doorways, windows, and staircases. I'd like the player to be able to move through the doorways and larger windows, as well as *up* the staircases. So now I have a million questions, for those who have made things like this before, or just generally know about 3D collisions. I have a fair amount of programming experience as a gamedev+network programmer, but I'm very new to 3D. So my questions are really from the perspective of "how do I do a thing in 3d?" and "how do I do this thing via babylon.js?" I've been doing blender tutorials for two weeks in preparation. Whats the general idea behind making a low-poly house (interior and exterior)... do I just model the whole thing inside and out in blender and import it? Should the house be one mesh or multiple? Is the mesh that I create in blender the same as the mesh I should use for collisions? Is this going to magically work via moveWithCollisions? What about staircases? Should they be a ramp instead of steps? What about the doors/windows/openings? Do these just work? How do I place the house down into the terrain? I've got a bunch of ideas for this, from flattening the terrain, to connecting the foundation mesh to the ground, to just giving the houses a big fat block of a foundation and sinking it into the heightmap.. but would love to hear some options.
  12. timetocode

    Math for moving rotated objects

    I *think* those are all the same for me. I'm not completely certain, but I tried logging several things out and there were either the same, close enough (floating point) or undefined. I noticed that moving the shooting logic to after the movement logic produces a perfect synchronization. However, while that does get the `forward vector` identical between server and client, it is incorrect for gameplay reasons (the shot needs to occur before moving each frame, not after). So I've tried moving 0,0,0 before shooting, and it seems to fix everything: mesh.moveWithCollisions(BABYLON.Vector3.Zero()) For some reason this fixes everything. I can just use that just fine... but is it indicating that I've done something else wrong? Here's the full code, if anyone is interested. Sorry its a bit much to read, and all the key information is probably above somewhere. move(command, tick) { // primary attack if (command.primary) { if (this.weaponSystem.canFire()) { this.weaponSystem.fire() this.mesh.moveWithCollisions(BABYLON.Vector3.Zero()) // fix let f = BABYLON.Vector3.TransformCoordinates(BABYLON.Vector3.Forward(), this.mesh.getWorldMatrix()) let d = f.subtract(this.mesh.position) let v = d.normalize() let ray = new BABYLON.Ray(this.mesh.position, v, 100) var hit = this.scene.pickWithRay(ray, (mesh) => { // don't hit yourself if (mesh === this.mesh) { return false } return true }) v = hit.pickedPoint if (v) { // abstraction of firing logic, b/c server and client do different things when they fire // client: draws a debug ray // server: registers damage against a player this.wInterface.fire(this.id, this.x, this.y, this.z, v.x, v.y, v.z) } } } this.weaponSystem.update(command.delta) let camVector = new BABYLON.Vector3( // TODO: rename these variables to camVectorX,Y,Z; they are no longer rotations command.rotationX, command.rotationY, command.rotationZ ) this.mesh.lookAt(this.mesh.position.add(camVector.negate())) let unit = BABYLON.Vector3.Zero() if (command.forward) { unit.z += 1 } if (command.backward) { unit.z -= 1 } if (command.left) { unit.x -= 1 } if (command.right) { unit.x += 1 } unit.normalize() let matrix = BABYLON.Matrix.RotationAxis(BABYLON.Axis.Y, this.mesh.rotation.y) let heading = new BABYLON.Vector3( unit.x * this.speed * command.delta, unit.y * this.speed * command.delta, unit.z * this.speed * command.delta, ) let movement = heading.clone() if (command.jump) { // jetpack movement.y += 10 * command.delta } else { // gravity-ish movement.y -= 10 * command.delta } let movementVector = BABYLON.Vector3.TransformCoordinates(movement, matrix) this.mesh.moveWithCollisions(movementVector) // collision against terrain let y = this.scene.ground.getHeightAtCoordinates(this.mesh.position.x, this.mesh.position.z) // suspicious of this... it is movement that occurs *after* moveWithCollisions // it can't be 100% of the problem tho, because this only occurs when resting on the ground // and desync issues still occur while flying far above the mesh if (this.mesh.position.y < y + 0.5) { this.mesh.position.y = y + 0.5 } } Attached is now a picture of it working. It is a little hard to see but the red & white debug-shot-tubes are overlapping perfectly now.
  13. timetocode

    Math for moving rotated objects

    I've added firing a Ray forward to the above logic and I'm running into a problem. I begin calculating the direction of the ray similarly to the forward-ish logic from above: let f = BABYLON.Vector3.TransformCoordinates(BABYLON.Vector3.Forward(), mesh.getWorldMatrix()) console.log('f', f) console.log('mesh.position', this.mesh.position) console.log('mesh.rotation', this.mesh.rotation) Problem: `f` is different on server and client, even though mesh.position and mesh.rotation are the same. So I guess mesh.getWorldMatrix() is based off more than just position and rotation..is that correct? What other data do I need to synchronize? I think some part of the transform is out of sync. Log output from server: [1] f t { [1] x: 87.71782332658768, [1] y: 28.766663193702698, [1] z: 39.23026758432388 } [1] mesh.position t { [1] x: 87.25551778257102, [1] y: 29.250780211047058, [1] z: 38.58501679257934 } [1] mesh.rotation t { x: 0.6739999993520329, y: 0.7990000362848866, z: 0 } Log output from client: f t {x: 87.81546431779861, y: 28.62666380405426, z: 39.12993723154068} mesh.position t {x: 87.25551778257102, y: 29.250780211047058, z: 38.58501679257934} mesh.rotation t {x: 0.6739999993520329, y: 0.7990000362848866, z: 0} All there is to see amongst these numbers are that mesh.position and mesh.rotation are the same on server and client, but calculating `f` via mesh.getWorldMatrix produces different results. I've attached a picture of the difference. Rays created on the clientside are rendered in white, and rays created on the serverside on rendered in red. The correct result would be that the white and red rays overlap perfectly.
  14. timetocode

    Math for moving rotated objects

    Iv'e been experimenting with a few things, mostly centered around TransformCoordinates, and Matrix.RotationAxis(axis, mesh.rotation). I've been trying this for forward on a mesh: let forward = BABYLON.Vector3.TransformCoordinates(BABYLON.Vector3.Forward(), mesh.getWorldMatrix()) let dir = forward.subtract(mesh.position).normalize() Then this for forward on a camera: let cameraRay = camera.getForwardRay().direction And then for multiplayer, when moving a player on the server, I send the cameraRay and the controls over the network and move forward/back and strafe left/right like this: let camVector = new BABYLON.Vector3( command.cameraVectorX, command.cameraVectorY, command.cameraVectorZ ) // rotates the player this.mesh.lookAt(this.mesh.position.add(camVector.negate())) let unit = BABYLON.Vector3.Zero() if (command.forward) { unit.z += 1 } if (command.backward) { unit.z -= 1 } if (command.left) { unit.x -= 1 } if (command.right) { unit.x += 1 } unit.normalize() let matrix = BABYLON.Matrix.RotationAxis(BABYLON.Axis.Y, this.mesh.rotation.y) let heading = new BABYLON.Vector3( unit.x * this.speed * command.delta, unit.y * this.speed * command.delta, unit.z * this.speed * command.delta, ) let movement = heading.clone() // no need to clone if (command.jump) { // no-accel jetpack movement.y += 10 * command.delta } else { // really fake no-accel gravity movement.y -= 10 * command.delta } let movementVector = BABYLON.Vector3.TransformCoordinates(movement, matrix) this.mesh.moveWithCollisions(movementVector) // and then for shooting, ^ `command` is the network object holding the data from the client It is getting a little better, still not sure if these are good ways to do things or not
  15. timetocode

    Math for moving rotated objects

    What does one pass to getDirection?