Jump to content

A few hours into my first game, it does what it's supposed to do, but I think i'm doing it wrong...


reyesjmf
 Share

Recommended Posts

Hello everyone! New guy here.

Yesterday, I decided i'm gonna learn how to make games! With a game idea in mind, I took the plunge. I have some background in programming but I have little to no stock knowledge of Javascript. I also just learned about how game frameworks/engines work. The game I have in mind is very ambitious for my skill level so I decided to scale things down and build it up as I gain more knowledge and experience.

First off, I knew I wanted a world with a closed door in it. A timer counts down (random from 1 to 3 second). Door opens. Shows a random mob from a choice of three... This is where I stopped to reevaluate if i'm actually doing this RIGHT.

Here is what I have so far:

var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update, render: render  });

function preload() {
	game.load.image('bg', 'assets/bg.png');
    game.load.image('door_open', 'assets/door_open.png');
    game.load.image('door_closed', 'assets/door_closed.png');
    game.load.image('mob1', 'assets/mob1.png');
    game.load.image('mob2', 'assets/mob2.png');
    game.load.image('mob3', 'assets/mob3.png');
    
}

var doorTimer, mobType;

function create() {

	game.add.image(0, 0, 'bg');
	game.add.image(50, 180, 'door_closed');
	doorTimer = game.time.events.add(Phaser.Timer.SECOND * game.rnd.integerInRange(1, 4), showMob, this);
}


function showMob() {

    game.add.image(50, 168, 'door_open');
    mobType = game.rnd.integerInRange(1, 3);
     if (mobType == 1)
    {
        game.add.image(75, 248, 'mob1');
    }
    if (mobType == 2)
    {
        game.add.image(50, 248, 'mob2');
    }
    if (mobType == 3)
    {
        game.add.image(55, 248, 'mob3');
    }


}


function update() {

	
}

function render() {



  game.debug.text("Time until show mob: " + game.time.events.duration, 32, 32);
  game.debug.text("Mob type: " + mobType, 32, 64);
    

}

Eventually, I want players to be able to click/tap on the mob to kill it. Closing the door. Resetting the timer. Mobs will eventually have their own attacks in the future. etc etc. I hope you get the picture.

Questions:
1. Am I on the right track?
2. Am I programming with a very primitive / simplistic approach?
3. How do I call and use a function from inside a function? Say after showing the mob in the showMob function, how do i make the mob do things?
4. When do I call and use a function rather than using the 'update' function?

Sorry for the lengthy post! I really want to learn how to make games but i'm sure gonna need a lot of help!

Thanks!


 

Link to comment
Share on other sites

Hi, I'm not the most experienced in game development out there, but if I may:

1. There seems to be nothing wrong with your approach, you might want to focus on finishing your game as first goal, which let you learn how Phaser works.

2. If it fits the project scope, you might prefer to use a very simple programming style. Otherwise, you seem to take the path towards functional programming.

3. Javascript program often uses callback functions, you might want to check that key-word. But with your actual code, showMob handles only pictures to show, and pictures don't do anything. You might want to create a mobAttack() function that uses the mobType as a parameter. Also you might want to check for Phaser.Sprites, which extends the possibility of the Phaser.Image class, with a lot of useful features for video games (like animations).

4. Update function is run by Phaser continuously, so you can totally call functions inside that 'loop', and it will be triggered each frame. You can also check how Phaser.Signals work so you can trigger functions only after particular events occurs.

Keep it up!

Link to comment
Share on other sites

1. Looks to me like a solid start. Github's early unofficial motto was something like: 'if it works, its great', which is a play on a much older programming motto, something that hints at agile processes, whereby, if it works then you're doing it right, whether it 'looks' like great code is totally subjective, whether its easily extensible or debuggable is a little more quantifiable but can still be a contentious issue amongst different developers.

Agile processes would largely have you building working software, iterating from there through working versions of software, until you get to a point where you cease working on it. To that end you're doing great I think.

2. Who cares? honestly? do what makes sense for you. If you want to try a different approach as you're learning then crack on, otherwise you're doing things right (in my view) by primarily getting things working.

For what its worth, my view is that everything looks very neat and tidy, things are clear and you're idiomatic as much as Phaser is concerned which will give you great benefits as you progress using that framework.

I favour a very functional approach, you're not quite in to FP (functional programming) land but you're doing a really good thing by breaking things in to functions, I can see one obvious area where you can take this a step further, explained next:

3. Functions are first class citizens in JS and can be thrown around with great flexibility, but, most languages will let you call 'global' (or unattached) functions anywhere in your code (if you're from a strict OOP background then most of your functions may live in objects).

At the moment you have a load of functions in the global namespace, they can be called from anywhere, this is a great position to be in, only surpassed (in my view) by using some sort of modularisation, but you don't need that here, build out from what you have:

function create () {...}

var mobTypes = [
  {
    name: 'mob1',
    offset: [75, 248]
  }, {
    name: 'mob2',
    offset: [50, 248]
  }, {
    name: 'mob3',
    offset: [55, 248]
  }
  
]

function addMob (id) {
  var mob = mobTypes[id]
  game.add.image(mob.offset[0], mob.offset[1], mob.name)
}

function showMob () {
  addMob(game.rnd.integerInRange(1, 3))
}

function showDoor () {
  game.add.image(50, 168, 'door_open')
}

function update () {...}

All I've done here is extracted your mob data into its own structure and then called a function that handles that data and performs an action. This is not strictly functional programming (it creates side effects, read up on those if you want to get into FP, for now, I'd say ignore it) but has split things up into discrete functions that attend to just one job at a time, hence I extracted the door rendering code to its own function and removed it from showMob, this adheres to a principle that a process (in this case, a function) should do one job and one job only, it is then up to the consumer to compose those functions together to create more complex behaviour, in your case, you just need to call each function to draw a door and then a mob.

There are loads and loads of advantages here. Functions have an implicit api by nature of their inputs (parameters), you can not specify what a JS function will return. If you restrict what each function does then you restrict the number of parameters it needs to be useful, I mentioned side effects earlier and you might want to look up Pure Functions (or referential transparency) which states that a function should not touch the outside world, you can safely ignore it for now but keep it on the back burner as its incredibly useful to know about.

4. Not sure what you're after here, Phaser states have a number of 'life-cycle' methods, defined by the framework, of which update is one, if you want a function to run on a tick then use the 'update' Phaser wants you to, it does the heavy lifting of managing all that stuff, roll with it. If you use a framework then you really need to use it how it is designed to be used, otherwise its utility for you greatly diminishes, frameworks tend to be very good at what they do and will resist being changed from that purpose.

 

 

Link to comment
Share on other sites

13 hours ago, raaaahman said:

Hi, I'm not the most experienced in game development out there, but if I may:

1. There seems to be nothing wrong with your approach, you might want to focus on finishing your game as first goal, which let you learn how Phaser works.

2. If it fits the project scope, you might prefer to use a very simple programming style. Otherwise, you seem to take the path towards functional programming.

3. Javascript program often uses callback functions, you might want to check that key-word. But with your actual code, showMob handles only pictures to show, and pictures don't do anything. You might want to create a mobAttack() function that uses the mobType as a parameter. Also you might want to check for Phaser.Sprites, which extends the possibility of the Phaser.Image class, with a lot of useful features for video games (like animations).

4. Update function is run by Phaser continuously, so you can totally call functions inside that 'loop', and it will be triggered each frame. You can also check how Phaser.Signals work so you can trigger functions only after particular events occurs.

Keep it up!

Thank you! In #3, when you said create a mobAttack function with mobType as a parameter, did you mean like this:

function showMob() {

    game.add.image(50, 168, 'door_open');
    mobType = game.rnd.integerInRange(1, 3); 
     if (mobType == 1)
    {
        game.add.image(75, 248, 'mob1')
        mobAttack(mobType)
        
    }
    if (mobType == 2)
    {
         game.add.image(50, 248, 'mob2');        
         mobAttack(mobType)
        
    }
    if (mobType == 3)
    {
         game.add.image(55, 248, 'mob3');
         mobAttack(mobType)
        
    }
   }

function mobAttack(1) { 

    
//add all the variables and values (attack speed, life, etc.) of the mob 1 here. 

        
    }

function mobAttack(2) { 

    //add all the variables and values (attack speed, life, etc.) of the mob 2 here
        
    }

function mobAttack(3) { 

    //add all the variables and values (attack speed, life, etc.) of the mob 3 here
        
    }

I haven't tested this if it works. (Probably wouldn't. :-P) Just making sure im understanding it right.

Link to comment
Share on other sites

Thank you for the reply! I've been reading it slowly again and again trying to make sense of everything that you said.

I hope im understanding your code correctly. Please allow me to break it up section by section. Kindly correct me if im wrong.

 

function create () {...}

//this part declares a variable named mobType with multiple values for each mob, giving them a name and an offset position. ???
var mobTypes = [
  {
    name: 'mob1',
    offset: [75, 248]
  }, {
    name: 'mob2',
    offset: [50, 248]
  }, {
    name: 'mob3',
    offset: [55, 248]
  }
  
]

//a function that renders a mob on the screen based on the ID picked randomly in the next function below. ???
function addMob (id) {
  var mob = mobTypes[id]
  game.add.image(mob.offset[0], mob.offset[1], mob.name)
}

//a function that actually calls the function above it to display the mob based on the randomized number. the number being its ID. ???
function showMob () {
  addMob(game.rnd.integerInRange(1, 3))
}

//simply renders a door in the screen
function showDoor () {
  game.add.image(50, 168, 'door_open')
}

function update () {...}

Sorry, i'm so lost and confused. LOL

Link to comment
Share on other sites

1 minute ago, raaaahman said:

It seems that you need to read a little about functions and parameters. Hint: You're not supposing to pass a value when you declare a function, only when you run it. So you can use the same piece of code for an infinity of values. You might find simple lessons on Khan Academy that can teach you the basics about functions.

I did try to run the code I typed. Didnt work at all! LOL. I will brush up on functions and parameters ASAP. Thank you again!

Link to comment
Share on other sites

function mobAttack(1) { 

    
//add all the variables and values (attack speed, life, etc.) of the mob 1 here. 

        
    }

function mobAttack(2) { 

    //add all the variables and values (attack speed, life, etc.) of the mob 2 here
        
    }

This almost looks like function or method overloading, it isn't quite, but JS can't do that anyway!!

If you do go down the route of declaring your entities then I'd probably suggest something like:

function basicAttack (mob) {
  // do stuff
}

function specialAttack (mob) {
  // do stuff
}

var mobTypes = [
  {
    name: 'mob1',
    offset: [75, 248],
    attack: basicAttack
  }, {
    name: 'mob2',
    offset: [50, 248],
    attack: basicAttack
  }, {
    name: 'mob3',
    offset: [55, 248],
    attack: specialAttack
  }
]

This is starting to get a little more advanced at this stage.

The `basic` and `special` attacks take a parameter, a `mob`, this is so they are a little separated from things, making it easier to refactor or reuse later. I've then passed a reference to this function to the mobs declared in the `mobTypes` array, notice the lack of '()', this is important. '()' invokes the function, executing it, and returning anything is might return (in our case there are no returns from the function which means it'll returned 'undefined'). By passing a reference to the function we can call it later, its almost like a cheap mixin.

function addMob (id) {
  var mob = mobTypes[id]
  game.add.image(mob.offset[0], mob.offset[1], mob.name)
  mob.attack(mob)
}

It seems slightly odd to pass an instance of itself to a instance method, but, this does actually make the functions more flexible. Feel free to investigate with binding the function to the object so that `mob.attack(mob)` works maybe closer to how you want, pros and cons to each approach. You'd have to be careful though, currently that array of mobs is treated as base data, if you bound functions to the objects in there you might end up mutating objects in that array, which you likely don't want, you probably want a create mob function somewhere that creates a new instance of the mob (from the data in that array), then you have separate instances to work with. You're now getting firmly into the realm of creating objects, possibly classes if thats your thing.

As you add functionality you might want to have a little refactor and rename some of the functions, currently showing the mob causes it to be added and attack, when you create the mob you might want to work that out as there is a tight coupling at the moment i.e. you can not reuse the showMob function as it currently also attacks.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...