01271

Members
  • Content count

    13
  • Joined

  • Last visited

About 01271

  • Rank
    Member

Profile Information

  • Gender
    Male
  1. side note: sorry for making so many threads ( 3 in ~2 months ) but I have different issues and I think threads should be problem-centric rather than project-centric. So I wanted to know how to swap containers. Here's one of my containers. /** * a HUD container and child items */ game.inventoryContainer = game.inventoryContainer || {}; game.containers = game.containers || {}; game.containers.inventory = me.Container.extend({ init: function() { // call the constructor this._super(me.Container, 'init'); // persistent across level change this.isPersistent = true; // make sure we use screen coordinates this.floating = false; this.alwaysUpdate = false; this.visible = false; // give a name this.name = "inventoryContainer"; me.levelDirector.loadLevel("collision"); this.addChild(new me.ColorLayer("invbg", "#55233B"), 0); this.addChild(new game.invLayer1, 1); this.addChild(new game.ManagerEntity(0, 0), 2); game.pointers.containers.inventory = this; }, update: function(dt) { if (game.screenspace === this.name) { return this._super(me.Container, 'update', [dt]); } return false; }, draw: function(renderer, rect) { if (game.screenspace === this.name) { return this._super(me.Container, 'draw', [renderer, rect]); } return false; }, reactivate: function() { this.isRenderable = true; }, deactivate: function() { // this.isRenderable = false; // for (var i = this.children.length - 1; i >= 0; i--) { // this.children[i].pos.x +=1000; // } // me.game.world.removeChild(this); } }); It's added to the game via this.inventoryContainer = new game.containers.inventory(); me.game.world.addChild(this.inventoryContainer); How do I swap it out without losing its children? All I want is not to have it or its children use up processor cycles while another container is displayed. This I think would be interesting to other developers too.
  2. Sorry to be a bother I decided that container swaps are my best bet for changing things out. That way I can have the enemies moved from container to container easily. I don't know how to swap containers though. I tried making the container move 10000 pixels off the screen and I tried to change it by setting it all isRenderable = false; but neither worked, my other container just doesn't show up. What do I do to swap containers?
  3. The documentation for sprite animation is a bit out of date and a new method setAnimationSpeed(speed) or setAnimationSpeed(identifier, speed) should be added. I have a sprite and an animation this._super(me.Sprite, 'init', [339, 480, settings]); this.addAnimation('normal', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],100); this.setCurrentAnimation('normal'); This animates at a delay of 100 ms. I can change the animation delay by adding another parameter to the addAnimation method. I could not change the delay afterwards with the solutions I found. I have seen this answer: https://groups.google.com/forum/#!topic/melonjs/mk6mVcMoVKo but it is no longer relevant. this.anim.normal.animationspeed isn't a part of melonjs anymore and changing the other animationspeed settings doesn't have any effect. This is I think, because of a change from a 'global' animation delay to a frame-based animation delay. The new method for changing the speed of an animation should now be: setAnimationSpeed: function(identifier, speed) { for (var i = this.anim[identifier].frames.length - 1; i >= 0; i--) { this.anim[identifier].frames[i].delay = speed; } } There should be a note on the sprite and/or renderable pages about the animation speed able to be individually set on a per-frame basis. It could be useful for example if some character is looking to the left and then turning quickly to the right the frame set wouldn't have to look like this: [0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[1],[2],[3],[4],[5],[5],[5],[5],[5],[5],[5],[5] and could just have a long delay on the 0 and 5 frames. Every X.0.0 version should result in a new version of the wiki and documentation with the changes you're making. Relevant changes to each could be made to affected pages after.
  4. I now remember a year ago I used a bunch of hacks to make a similar situation happen. I used this.isPersistent = true; which saves something from being removed by a state change and I used the following code I made up after analyzing melonjs to change sprite images. I guess the only question I have left for this thread is if there's a better way to switch images on the fly that doesn't feel as if I'm trying to break the framework. this.renderable = new me.AnimationSheet(0, 0, { "image": me.loader.getImage('criminal'), "framewidth": 260, "frameheight": 129, "spacing": 0, "margin": 0 }); this.body.width = 260; this.width = 260; this.renderable.width = 260; this.renderable._width = 260; this.renderable._bounds._width = 260; this.renderable.resizeBounds(197, 260); this.updateBoundsPos(197, 260); This worked in melonJS-2.1.3 and probably works now in v4.0.0 (if not it shouldn't be too hard to change)
  5. What sort of framework would be used to switch the way that enemies function in an overworld/battle screen type game. The design is as follows: The player can see enemies on the game world screen and move around up, down, left, right. When the player touches an enemy both of them are transported to the fight screen. Intuitively, this screen should be a separate state. Now the problem arises: in the second fight screen state, the player and enemy behave very differently from normal. They stop moving up/down/left/right and take turns fighting. They are also shown from up close so their sprites are larger and more detailed. The battle can end without all of the combatants being defeated so values need to be persistent across both forms as the enemies & player should remain damaged post-combat. I tried this before without too much success, I had the characters pull out of the pool and then "realize" which state they're in and instantiating with a different image/modifying their behaviour accordingly. The problems with that solution were that the code was super clunky (if/else everywhere) and it didn't have persistent values when returning from a fight. Basically the enemies were managed by a director "class" that impersonated a real method of data storage. They had a prototype Finite State Machine controlling them too. var enemyDirector = { saveEnemyData: function() { var entList = me.game.world.getChildByName(); for (var i = entList.length - 1; i >= 0; i--) { if (typeof entList[i].selfData !== 'undefined') { var essentialEnemyData = { selfData: {}, pos: {} }; essentialEnemyData.selfData.modelname = entList[i].selfData.modelname; essentialEnemyData.selfData.hp = entList[i].selfData.hp; essentialEnemyData.selfData.maxhp = entList[i].selfData.maxhp; essentialEnemyData.selfData.uniqEnemyNum = entList[i].selfData.uniqEnemyNum; game.enemyTracker.enemyStates.push(essentialEnemyData); } } }, fightPositions: [], restoreEnemyFromSave: function(id) { for (var i = game.enemyTracker.enemyStates.length - 1; i >= 0; i--) { if (game.enemyTracker.enemyStates[i].selfData.uniqEnemyNum === id) { createPlaceEnemySprite(0, 0, game.enemyTracker.enemyStates[i], true); } } }, restoreAllEnemies: function() { for (var i = game.enemyTracker.enemyStates.length - 1; i >= 0; i--) { enemyDirector.restoreEnemyFromSave(game.enemyTracker.enemyStates.selfData.uniqEnemyNum); } }, restoreFightEnemies: function() { for (var i = game.data.enemyForFight.length - 1; i >= 0; i--) { enemyDirector.restoreEnemyFromSave(game.data.enemyForFight[i]); } } }; There has to be a better way but though I'm experienced in Javascript I'm not experienced in solving data/interaction models for JS games. I'm thinking for the enemy behaviour I can use my finite state machine to simply have the enemy move how I want him to in any situation but the sprites changing on state change & data persistence is more of a problem.
  6. Thanks for that. I've got another question now, I've got a finite state machine working but I want to have it pass variables to the functions. I want my character to wait for an arbitrary amount of seconds before doing his next action. wait: function(seconds) { if (this.waitTimer !== null) { this.setTimeout(function() { this.waitTimer = null; this.next(); return true; }.bind(this), seconds * 1000); } }, Yet this is inherently opposed to the previous design philosophy of having each state in a list like this: tackle: [this.moveToEnemy, this.wait, this.attackEnemy, this.returnHome] With such a list, am I doomed to have each "state" in a hash with the values I'll run on it? Is it possible to store them with their future use values without doing a hack job of it? IE tackle: [this.moveToEnemy, this.wait(5), this.attackEnemy(10), this.returnHome]
  7. I can't believe I keep running into problems like this. Ok so I have the following, but there's a scope issue with the array. When I try to use my code, I get an error that 'TypeError: this.getEnemyPosition is not a function[Learn More]'. This is because for some reason, the scope for 'this' in the procedures/states arrays is local to the array, as if I'd been using curly brackets. moveToEnemy: function(dt) { console.log(this); gives me Array [ game.PlayerFightEntity<.moveToEnemy(), game.PlayerFightEntity<.attackEnemy(), game.PlayerFightEntity<.returnHome() ] fsm.js:76:3 Am I doomed to referring to functions externally by calling game.PlayerFightEntity.whatever instead of this.whatever? Doesn't seem like it would scale well. var FSM = { "fsmReset": function() { /* Reset the state machine index */ this.fsmIndex = 0; }, "fsmUpdate": function(dt) { /* Call current state */ this.states[this.fsmIndex](dt); }, "next": function(dt) { /* Advance to the next state */ this.fsmIndex = (this.fsmIndex + 1) % this.states.length; if (dt) { // If Delta-Time is provided, call the next state immediately this.fsmUpdate(dt); } } }; game.PlayerFightEntity = me.Entity.extend({ init: function(x, y, settings) { settings.image = me.loader.getImage("playerfight"); settings.width = 128; settings.height = 128; this._super(me.Entity, "init", [x, y, settings]); this.alwaysUpdate = true; // create a bunch of different variables to hold for FSM position choosing. this.targetedEnemyID = 0; this.home = { x: 100, y: 100 }; /* Create a list of states for FSM */ this.procedures = { tackle: [this.moveToEnemy, this.attackEnemy, this.returnHome] }; this.states = [ this.idle ]; this.fsmReset(); game.playerFight = this; }, update: function(dt) { // console.log(this.body.pos, "position"); /* Update FSM (calls the current state and advances states as necessary) */ this.fsmUpdate(dt); this.body.update(dt); return this._super(me.Entity, "update", [dt]); }, /* State implementations */ idle: function(dt) { // if (this.timeToChargeEnemy()) { // this.next(); // } }, getEnemyPosition: function() { return game.fightEnemies[this.targetedEnemyID].position; }, moveToEnemy: function(dt) { console.log(this); var enemyPos = this.getEnemyPosition(); this.walkToPosition(enemyPos, dt); if (this.enemyInAttackRange()) { this.next(); } }, attackEnemy: function(dt) { this.attack(); if (this.timeToReturnHome()) { this.next(); } }, returnHome: function(dt) { this.walkToPosition(); if (this.madeItHome()) { this.next(); } }, stopMoving: function(dt) { this.body.vel.set(0, 0); this.next(dt); }, /* Convenience methods used by the state implementations */ timeToChargeEnemy: function() { /* TODO */ return true; }, enemyInAttackRange: function() { /* TODO */ return true; }, timeToReturnHome: function() { /* TODO */ return true; }, madeItHome: function() { /* TODO */ return true; }, walkToPosition: function(x, y) { /* TODO; something like this ... */ var angle = Math.atan2(target.y - this.pos.y, target.x - this.pos.x); var ySpeed = Math.sin(angle) * speed; var xSpeed = Math.cos(angle) * speed; this.body.vel.y -= this.body.accel.y * me.timer.tick; this.body.vel.y += this.body.accel.y * me.timer.tick; }, attack: function() { /* TODO */ }, startProcedure: function(actionName) { this.states = this.procedures[actionName]; }, selectEnemy: function(enemyID) { this.targetedEnemyID = enemyID; } }, FSM);
  8. I knew it was pseudocode but I also didn't know how any of the mixins worked, couldn't evaluate if it was correctly made or not x.x thanks.
  9. the fsmreset is from the FSM class that parasyte wrote. I can't find any documentation on extending classes like parasyte did. It makes it hard for me to know exactly what is going on. I took the SoldierFSM and the FSM classes above and put them in melonjs to no avail because when the FSM class doesn't have an init, it screws over the SoldierFSM with an error and when it does, the SoldierFSM can't run his own init function he just runs the parents'. At the end of the SoldierFSM class there's a "}, FSM);" so I know that's supposed to be how it's inheriting from the parent but I don't see overrides or inheritance working well on it so far.
  10. Well unfortunately the registration is already there, it's pulling into the game so it seems like it's the extension part isn't working. me.pool.register("SoldierFSM", game.SoldierFSM); was already in my game project file beside my other registrations. I tried to register the FSM itself me.pool.register("FSM", game.FSM); and it now complains about not having an init function. I think extending the object is a problem for this case, is there a class without an init it can extend?
  11. I've been slacking off. So far I've started integrating that previous solution into the game but there's a barrier to progress I've been encountering. If I have the FSM class extending the init function, I get the error and trace: TypeError: proto is undefined[Learn More] melonJS.js:4184:21 api.pull /melonJS.js:4184:21 game.BattleScreen<.onResetEvent /battle.js:18:26 me.ScreenObject<.reset /melonJS.js:12456:13 _switchState /melonJS.js:12651:17 bound _switchState self-hosted The battlescreen line is this: me.game.world.addChild(me.pool.pull("SoldierFSM", 100, 300, {width:128, height:128})); and if I add an empty init, I get TypeError: rect is undefined[Learn More] melonJS.js:8301:13 Quadtree.prototype.getIndex melonJS.js:8301:13 Quadtree.prototype.insert melonJS.js:8401:25 Quadtree.prototype.insertContainer melonJS.js:8358:21 api.update melonJS.js:2872:21 DebugPanel<.patchSystemFn/< debugPanel.js:290:17 singleton.patch/<.value</< melonJS.js:28481:39 _renderFrame melonJS.js:12613:13 If I have an init in the fsm with the _super init then my sprites are just invisible.
  12. That's pretty great! I've made my own solution of a "function queue" work while waiting and I'll get right on changing it into a finite state machine instead because that's what I wanted all along without knowing exactly how I'd do it. This is my currently working code for archival purposes, don't want to just leave without showing the world my solution, if someone were to come looking it wouldn't be nice to them. game.PlayerFightEntity = me.Entity.extend({ init: function(x, y, settings) { // call the constructor settings.height = 128; settings.width = 128; settings.image = me.loader.getImage("playerfight"); this._super(me.Entity, 'init', [x, y, settings]); game.playerFight = this; this.alwaysUpdate = true; //target types = single, number, all, self. var speed = 0; this.animationState = 'home'; this.queue = []; this.home = this.pos; this.targetPosition = this.home; this.startAttack = function(attackIndex, enemyIndex) { console.log('started attack.'); this.attacks[attackIndex].activate(enemyIndex); }; this.attacks = [{ target: 'single', name: 'tackle', aspect: 'physical', damage: 10, activate: function(enemyIndex) { this.targetPosition = game.fightEnemies[enemyIndex].pos; this.queue = [ function() { return this.battleFunctions.moveTo(this.targetPosition); }.bind(this), function() { return this.battleFunctions.applyDamage(enemyIndex); }.bind(this), function() { return this.battleFunctions.returnHome(); }.bind(this) ]; }.bind(this) }]; this.runQueue = function() { var success = false; if (this.queue.length > 0) { success = this.queue[0](); if (success) { this.queue.shift(); } } }; this.battleFunctions = { applyDamage: function(enemyIndex) { console.log("attack!"); game.fightEnemies[enemyIndex].takeDamage(10); return true; }, moveTo: function(target) { var speed = 10; var angle = Math.atan2(target.y - this.pos.y, target.x - this.pos.x); var ySpeed = Math.sin(angle) * speed; var xSpeed = Math.cos(angle) * speed; this.pos.y += ySpeed; this.pos.x += xSpeed; return (Math.abs(this.pos.x - target.x) < 3 && Math.abs(this.pos.y - target.y) < 3); // check if we've reached the point or even good enough. }.bind(this), moveToEnemy: function(enemyIndex) { return this.moveTo(game.fightEnemies[enemyIndex].position); }, returnHome: function() { return this.moveTo({ x: 100, y: 300 }); } }; function attack(enemyIndex, attackID) { console.log('attack!'); // setTimeout(this.endAttack(), 3000); this.attacks[attackID].activate(enemyIndex); } function endAttack() { // clean up stuff after attacking game.activeMenuSelector = 1; } }, update: function(dt) { this.runQueue(); this.body.update(dt); return true; }, onCollision: function(response, other) { // Make all other objects solid return false; } });
  13. I wanted to avoid a pyramid of if else and setTimeOut too, what I want to do is have a character make his way across the screen, from his "home" position, go to the enemy's position, apply damage, and then return. So far I've been looking into promises to have code execute one after the other but that doesn't work with the game loop, a promise is a promise and it's just good for one loop operation. Then I started working on a queuing function that runs a function every iteration until it returns true but it's spiralled out of control for complexity. Code is included below. I'm aware that my function for moving a player to a point doesn't work and that there are issues but those are things I can solve, what I can't solve is how do I make a sane, clean method for having the player queue through different actions to take? game.PlayerFightEntity = me.Entity.extend({ init: function(x, y, settings) { // call the constructor settings.height = 128; settings.width = 128; settings.image = me.loader.getImage("playerfight"); this._super(me.Entity, 'init', [x, y, settings]); game.playerFight = this; this.alwaysUpdate = true; //target types = single, number, all, self. var speed = 0; this.animationState = 'home'; this.queue = []; this.home = this.pos; this.targetPosition = this.home; this.startAttack = function(attackIndex, enemyIndex) { console.log('started attack.'); this.attacks[attackIndex].activate(enemyIndex); }; this.attacks = [{ target: 'single', name: 'tackle', aspect: 'physical', damage: 10, activate: function(enemyIndex) { this.target = game.fightEnemies[enemyIndex].position; this.queue = [ function() { this.battleFunctions.moveTo(this.targetPosition); return false; }.bind(this), function() { return this.battleFunctions.applyDamage(enemyIndex); }.bind(this), function() { return this.battleFunctions.moveTo(this.home); }.bind(this) ]; }.bind(this) }]; this.runQueue = function() { var success = false; console.log(this.queue.length); if (this.queue.length > 0) { console.log(success); success = this.queue[0](); if (success) { this.queue.shift(); } } }; this.battleFunctions = { applyDamage: function(enemyIndex) { game.fightEnemies[enemyIndex].damage(10); }, moveTo: function(target) { var angle = Math.atan2(target.y - y, target.x - x); var ySpeed = Math.sin(angle) * speed; var xSpeed = Math.cos(angle) * speed; this.pos.x *= xSpeed; this.pos.y *= ySpeed; console.log(target, angle, xSpeed); return (Math.round(this.pos.x) == target.x && Math.round(this.pos.y) == target.y); // check if we've reached the point or even good enough. }.bind(this), moveToEnemy: function(enemyIndex) { this.moveTo(game.fightEnemies[enemyIndex].position); }, returnHome: function() { this.moveTo({ x: 10, y: 10 }); } }; function attack(enemyIndex, attackID) { // setTimeout(this.endAttack(), 3000); this.attacks[attackID].activate(enemyIndex); } function endAttack() { // clean up stuff after attacking game.activeMenuSelector = 1; } }, update: function(dt) { this.runQueue(); this.body.update(dt); return true; }, onCollision: function(response, other) { // Make all other objects solid return false; } });