Great stuff Frank! Whether in this series or somewhere else, it would be cool to see how an area or world map is slowly revealed as the player moves around. I was watching this video (ua-cam.com/video/6CKW7tLv5e0/v-deo.html) and he uses the P5.js library. And I thought, I bet Frank don't need no stinkin' library! (Since you are the canvas master! Nothing wrong with using libraries!). Anyway, I watched that video and wondered if you wanted a challenge or video idea :) Thanks again!
@@cwlindWX Hi Clark, I could do that scratch card effect with vanilla, it wouldn't actually take much more code than using a library. Not sure how I would implement this particular effect into a game. I could for example do that with each level you uncover a bigger portion of the game world. I will put it on my list, let's see if I can include something like that in a future project, thank you for the idea.
Thank you very much, Frank, for a useful and unique course, you opened the world of web game development to me) I noticed one thing about fps control that might be helpful: when we check "if (timer > interval)..." we get error in one deltaTime iteration Because, for example, fps = 20 => interval = 1000/20 = 50 And if 16.666666 * 3 then we get 49.999998 It's almost an early 50, but we technically need another iteration. So I suggest "if (timer >= interval - deltaTime)..." Found by accident while debugging player teleportation and mana replenishment timers hah Thanks again for your work, I wish you good luck in the growth of the channel) 🔥
Thanks Carl, I'm explaining some things in this series I haven't shown before, let's see if it's still beginner friendly, maybe my future projects will have to be a bit simpler. Glad you found some value
Dude I started this series when I started learning js but i didnt understand anything now i am much advanced and I restarted this, dude the way you explain and the easy way you code i love that
It feels good when you see yourself progressing doesn't it. I usually have to do each new technique 3 to 5 times before it starts making sense to me. Practice is important. Have fun with the project
random small improvement I found: this movement code for the background causes a small stutter when we reset the image back to x=0, this is because the game speed pushes x by 3 and sometimes pushes x past the ultimate width, so when we get set back to 0 you can see the stutter, we also did not account for the gamespeed in this scenario. To fix this, in the update method for Layer class, I instead increment x by the width of the background image, and I also subtract the game speed to keep moving seamlessly. code: if (this.x < -this.width) this.x += this.width - (this.game.speed*this.speedModifier);
Loving this series so far, and indeed all your videos having recently discovered your channel. I'm a JS developer of business applications with some 15 years experience and it's nice to be able to follow along with your well-written and explained code to use my skills in a different way. Great job! +1 sub, +1 notification from me.
Hi Ben, nice to see that my videos could be interesting even for experienced developers. Appreciate your support, thank you for letting me know you found some value!
Tip: when using "Enum types" like states, you can use Objet.freeze to ensure the values doesn't change: like this: const states = Object.freeze({SITTING:0, RUNNING:1});
It's just a simple object and a couple of if else statements. I'm doing a replenishing ammo in the next class releasing next week, it's the same thing, I will explain it there.
Thanks! I'll need to use that deltaTime technique at some point. Just one question: doesn't this make it refresh slower than the frameInterval? (because you use if(this.frameTimer>this.frameInterval). It would be nice to see a plot of the this.frameTimer values as they accumulate over time. I assume the average value to be something like (frameInterval+deltaTime)/2.
Yes. This technique is not completely accurate because, there is a leftover part of deltaTime value after every reset. For complete cross device behaviour that leftover value would have to be accounted for but I think for the purposes of a project like this it works well enough.
Hi Frank, Awesome course, i'm followig it and I'm enjoying it very much. I just have one question, why do we need to set up a server? I've tried launching the game without the server, just opening the index.html file and it does not work. I'll thank you if you can clearify this.
Hi Aitor, I briefly mention the reason in the beginning, it's because we are splitting code into modules, multiple files, and we are exporting and importing. This only works when run though a server. If you want it to work locally you would have to put all javascript into a single file. it could be challenging if you are a beginner because the order in which you declare classes is important, they don't hoist
@@Frankslaboratory thank you very much, Frank. Very well explained. I watched both videos in their entirety and missed that comment, my fault 😅. Keep up the good work!!
There are more and more companies doing 2D browser games. If you're good with JavaScript you can get many jobs. Also the logic is easily transferable to other programming languages if needed. When you get a job in a good company they don't care what language you know, as long as you are good at it, they will train you in their specific stack when you join
@@Frankslaboratory I see we also are not using deltaTime in the update class in player.js. I skipped the states etc as I plan to use a still image and not animations etc.
@@h4m2_ deltaTime can cap anything, it just measures milliseconds so you can use that to trigger periodic event to cap fps of a sprite on a single object or of the entire game, it's the same technique, just needs a small adjustment. In the class I just finished filming I used it that way so that it caps everything. It will cover high refresh rate screens
I followed along, step by step, but when it's time to see the images scrolling in the background i see my character in a perfect white void 😅 i think the problem is here: update(){ if (this.x < - this.width) this.x = 0; else this.x -= this.game.speed * this.speedModifier; } with this function, if i console log the array entries with: console.log(this.backgroundLayers[0].x, this.backgroundLayers[0].y); i noticed that the x is NaN and so the program dosen't draw the images. I checked your code and my code several times but it's EXACTLEY the same. How it's possible that is NaN?
Its NaN because one the line where you are calculating that x value, one of the operands is undefined. You need to track that logic until you find where you made a typo
@@Frankslaboratory I assigned this.x = 0 in the Layer class constructor and then i used: update(){ if (this.x < - this.width) this.x = 0; else this.x -= this.game.speed * this.speedModifier; }. i check if is NaN, i opened the object in console, the problem is the x NaN and the entire update method give me undefined. If i took out the minus sign before the first this.width in the if condition x is 0 and the images are there but dosen't move.
Hi Ivan, there are indeed many use cases for this technique. For example player can have a special animation loop that plays once when entering or exiting each state, if you have sprite sheet for that. Also it can be used to create pause/play/menu functionality when applied to the main game object. I might show how to do all of that later.
You can control frame rate with delta time calculation. Comparing old and the current timestamp. I do it in most of my game dev classes, didn't do it in this one
Part 1 here, have fun! :D ua-cam.com/video/c-1dBd1_G8A/v-deo.html What else should we implement
Great stuff Frank! Whether in this series or somewhere else, it would be cool to see how an area or world map is slowly revealed as the player moves around. I was watching this video (ua-cam.com/video/6CKW7tLv5e0/v-deo.html) and he uses the P5.js library. And I thought, I bet Frank don't need no stinkin' library! (Since you are the canvas master! Nothing wrong with using libraries!). Anyway, I watched that video and wondered if you wanted a challenge or video idea :) Thanks again!
@@cwlindWX Hi Clark, I could do that scratch card effect with vanilla, it wouldn't actually take much more code than using a library. Not sure how I would implement this particular effect into a game. I could for example do that with each level you uncover a bigger portion of the game world. I will put it on my list, let's see if I can include something like that in a future project, thank you for the idea.
Thank you very much, Frank, for a useful and unique course, you opened the world of web game development to me)
I noticed one thing about fps control that might be helpful:
when we check "if (timer > interval)..." we get error in one deltaTime iteration
Because, for example, fps = 20 => interval = 1000/20 = 50
And if 16.666666 * 3 then we get 49.999998
It's almost an early 50, but we technically need another iteration.
So I suggest "if (timer >= interval - deltaTime)..."
Found by accident while debugging player teleportation and mana replenishment timers hah
Thanks again for your work, I wish you good luck in the growth of the channel) 🔥
@@Frankslaboratory Maybe making a platform game would be cool.
Frank, love the code refinements, I always learn something new from your excellent tutorials. Thanks for sharing your knowledge!
Thanks Carl, I'm explaining some things in this series I haven't shown before, let's see if it's still beginner friendly, maybe my future projects will have to be a bit simpler. Glad you found some value
Another masterpiece. Thanks Frank. Enjoy your weekend.
Nick! Have a great weekend :)
Hi Frank, I completed this part a few days ago...thank you!Very informative. I am HOOKED! 😀
This channel is a great find. Thank you for these excellent tutorials. Wishing you great success.
Hi Chris, nice to meet you, glad you found some value!
A lot of great information, wonderfully explained with useful on screen notes to help with understanding - good job as always Frank! (:
Hi Phoenixx, glad you find my new teaching style useful, still improving it :D
This finally made Enums make so much sense to me. Thank you so very much!!!!
I'm here to help!
Just discovered the channel! It is amazing!I'm learning a lot! Thank you so much!
Nice to meet you Erico!
I love the way you teach things :)
Díky. Skvělá práce. Skvělý tutoriál.
Cestina, to uz jsem dlouho nevidel :D Ahooj
Thank you so much mate. I’ve been looking for a way to create this for months !!
Happy to help! :)
Dude I started this series when I started learning js but i didnt understand anything now i am much advanced and I restarted this, dude the way you explain and the easy way you code i love that
It feels good when you see yourself progressing doesn't it. I usually have to do each new technique 3 to 5 times before it starts making sense to me. Practice is important. Have fun with the project
@@Frankslaboratory Yeah Agreed
This is my next challenge. Bring it on!
Thanks , You always deliver
Glad you found some value :D
random small improvement I found: this movement code for the background causes a small stutter when we reset the image back to x=0, this is because the game speed pushes x by 3 and sometimes pushes x past the ultimate width, so when we get set back to 0 you can see the stutter, we also did not account for the gamespeed in this scenario.
To fix this, in the update method for Layer class, I instead increment x by the width of the background image, and I also subtract the game speed to keep moving seamlessly. code:
if (this.x < -this.width) this.x += this.width - (this.game.speed*this.speedModifier);
Loving this series so far, and indeed all your videos having recently discovered your channel. I'm a JS developer of business applications with some 15 years experience and it's nice to be able to follow along with your well-written and explained code to use my skills in a different way. Great job! +1 sub, +1 notification from me.
Hi Ben, nice to see that my videos could be interesting even for experienced developers. Appreciate your support, thank you for letting me know you found some value!
Hi Frank, I'm loving your tutorial and learning so much. You started mobile controls but how do you add different states on mobile.
amazing video and explanation
Tip: when using "Enum types" like states, you can use Objet.freeze to ensure the values doesn't change: like this: const states = Object.freeze({SITTING:0, RUNNING:1});
Nice video sir. Thank you for the tutorials.
Happy to help Avinash, comment 5 sec after release haha, nice
Excellent!!
Hi Evaristo, I used state design pattern only because of your advice, otherwise this code would look very different ! :)
First, thanks for all the effort Frank
Hi Sertan, you are fast! :D
How did you do the energy?
It's just a simple object and a couple of if else statements. I'm doing a replenishing ammo in the next class releasing next week, it's the same thing, I will explain it there.
Thanks! I'll need to use that deltaTime technique at some point. Just one question: doesn't this make it refresh slower than the frameInterval? (because you use if(this.frameTimer>this.frameInterval). It would be nice to see a plot of the this.frameTimer values as they accumulate over time. I assume the average value to be something like (frameInterval+deltaTime)/2.
Yes. This technique is not completely accurate because, there is a leftover part of deltaTime value after every reset. For complete cross device behaviour that leftover value would have to be accounted for but I think for the purposes of a project like this it works well enough.
@@Frankslaboratory I see. Thank you, Frank!
Thank you Sir,
:)
Happy to help Kamal
thanks!!
Glad to help Jees
hi teacher, im doing same with you but my player not moving when he standing ?? maybe my chrome have problem??
Hi Frank,
Awesome course, i'm followig it and I'm enjoying it very much.
I just have one question, why do we need to set up a server? I've tried launching the game without the server, just opening the index.html file and it does not work. I'll thank you if you can clearify this.
Hi Aitor, I briefly mention the reason in the beginning, it's because we are splitting code into modules, multiple files, and we are exporting and importing. This only works when run though a server. If you want it to work locally you would have to put all javascript into a single file. it could be challenging if you are a beginner because the order in which you declare classes is important, they don't hoist
@@Frankslaboratory thank you very much, Frank. Very well explained. I watched both videos in their entirety and missed that comment, my fault 😅. Keep up the good work!!
hello teacher, the tutorial was very good, the game is beautiful🧐
Hi Alessandro, glad you found some value! :)
Can I find jobs in this direction? JS 2d games? Or is it just for fun?
There are more and more companies doing 2D browser games. If you're good with JavaScript you can get many jobs. Also the logic is easily transferable to other programming languages if needed. When you get a job in a good company they don't care what language you know, as long as you are good at it, they will train you in their specific stack when you join
is this game series finished?
Almost, there will be more episodes to finish the full game
how do you cap the fps so the game doesn't move as fast. I'm getting 144 fps because of my monitor and I find it way too quick.
Using delta time you set timer and interval for serving animation frames. I'll do in in the project I'm releasing this Friday
@@Frankslaboratory omg thank you so much for the quick reply. I'll try it right now! Doesn't this just cap the fps of the sprite?
@@Frankslaboratory I see we also are not using deltaTime in the update class in player.js. I skipped the states etc as I plan to use a still image and not animations etc.
@@h4m2_ deltaTime can cap anything, it just measures milliseconds so you can use that to trigger periodic event to cap fps of a sprite on a single object or of the entire game, it's the same technique, just needs a small adjustment. In the class I just finished filming I used it that way so that it caps everything. It will cover high refresh rate screens
@@Frankslaboratory thank you very much for this amazing content. I'm looking forward to Friday now!
I followed along, step by step, but when it's time to see the images scrolling in the background i see my character in a perfect white void 😅 i think the problem is here:
update(){
if (this.x < - this.width) this.x = 0;
else this.x -= this.game.speed * this.speedModifier;
}
with this function, if i console log the array entries with:
console.log(this.backgroundLayers[0].x, this.backgroundLayers[0].y);
i noticed that the x is NaN and so the program dosen't draw the images. I checked your code and my code several times but it's EXACTLEY the same. How it's possible that is NaN?
Its NaN because one the line where you are calculating that x value, one of the operands is undefined. You need to track that logic until you find where you made a typo
@@Frankslaboratory I assigned this.x = 0 in the Layer class constructor and then i used:
update(){
if (this.x < - this.width) this.x = 0;
else this.x -= this.game.speed * this.speedModifier;
}.
i check if is NaN, i opened the object in console, the problem is the x NaN and the entire update method give me undefined. If i took out the minus sign before the first this.width in the if condition x is 0 and the images are there but dosen't move.
YES! I did it you
you were right there was a typo! Thanks!
@@VeGaDrOnE glad you worked it out, nice! :)
Hi can someone help me in console say currentstate undefined please someone help me
Request tutorial idle game with js
Hi Rifan, what are some examples of idle game genre
Hello, how are you??
..excuse me, do you have a Java language course?
Hi Jasmin, I only do JavaScript, for Java there are some other experts here on UA-cam
@@Frankslaboratory
Can you advise me how to make an application for student absence please
My dog jumping soo fast on 165hz refresh rate screen and I can't find solution. Someone know what should I do?
You can use delta time to throttle the entire animation loop and max it at around 60fps. I did it in the game tutorial I released last week
I hate you. Now I feel not to have hours in a day to play with this new knowledge, lmao
Hi Ivan, there are indeed many use cases for this technique. For example player can have a special animation loop that plays once when entering or exiting each state, if you have sprite sheet for that. Also it can be used to create pause/play/menu functionality when applied to the main game object. I might show how to do all of that later.
@@Frankslaboratory Thank you so much for the ideas and for your free content. I've learned so much from you :)
Bro dog animating too fast how can i slow it
You can control frame rate with delta time calculation. Comparing old and the current timestamp. I do it in most of my game dev classes, didn't do it in this one
Hi sir please make this game installable
Hi, this is a browser game, not sure if JavaScript can do installable games, probably you need something like C+ for that?
Ok Sir
Can someone help me? My code not working...
background.js
class Layer {
constructor(game, width, height, speedModifier, image) {
this.game = game
this.width = width
this.height = height
this.speedModifier = speedModifier
this.image = image
this.x = 0
this.y = 0
}
update() {
if (this.x < -this.width) this.x = 0
else this.x -= this.game.speed * this.speedModifier
}
draw(context) {
context.drawImage(this.image, this.x, this.y, this.width, this.height)
context.drawImage(this.image, this.x + this.width, this.y, this.width, this.height)
}
}
export class Background {
constructor(game) {
this.game = game
this.width = 1667
this.height = 500
this.layer1image = document.getElementById('layer1')
this.layer2image = document.getElementById('layer2')
this.layer3image = document.getElementById('layer3')
this.layer4image = document.getElementById('layer4')
this.layer5image = document.getElementById('layer5')
this.layer1 = new Layer(this.game, this.width, this.height, 0, this.layer1image)
this.layer2 = new Layer(this.game, this.width, this.height, 0.2, this.layer2image)
this.layer3 = new Layer(this.game, this.width, this.height, 0.4, this.layer3image)
this.layer4 = new Layer(this.game, this.width, this.height, 0.8, this.layer4image)
this.layer5 = new Layer(this.game, this.width, this.height, 1, this.layer5image)
this.backgroundLayers = [this.layer1, this.layer2, this.layer3, this.layer4, this.layer5]
}
update() {
this.backgroundLayers.forEach(layer => {
layer.update()
})
}
draw(context) {
this.backgroundLayers.forEach(layer => {
layer.update(context)
})
}
}
player.js
import { Sitting, Running, Jumping, Falling } from "./playerStates.js"
export class Player {
constructor(game) {
this.game = game
this.width = 100
this.height = 91.3
this.x = 0
this.y = this.game.height - this.height - this.groundMargin
this.vy = 0
this.weight = 1
this.image = document.getElementById('player')
this.frameX = 0
this.frameY = 0
this.maxFrame
this.fps = 20
this.frameInterval = 1000/this.fps
this.frameTimer = 0
this.speed = 0
this.maxSpeed = 10
this.states = [new Sitting(this), new Running(this), new Jumping(this), new Falling(this)]
this.currentState = this.states[0]
this.currentState.enter()
}
update(input, deltaTime) {
this.currentState.handleInput(input)
// Horizpntal Movement
this.x += this.speed
if (input.includes('ArrowRight')) this.speed = this.maxSpeed
else if (input.includes('ArrowLeft')) this.speed = -this.maxSpeed
else this.speed = 0
if (this.x < 0) this.x = 0
if (this.x > this.game.width - this.width) this.x = this.game.width - this.width
// Vertical Movement
this.y += this.vy
if (!this.onGround()) this.vy += this.weight
else this.vy = 0
// Sprite Animation
if (this.frameTimer > this.frameInterval) {
this.frameTimer = 0
if (this.frameX < this.maxFrame) this.frameX++
else this.frameX = 0
} else {
this.frameInterval += deltaTime
}
}
draw(context) {
context.drawImage(this.image, this.frameX * this.width, this.frameY * this.height, this.width, this.height, this.x, this.y, this.width, this.height)
}
onGround() {
return this.y >= this.game.height - this.height - this.game.groundMargin
}
setState(state, speed) {
this.currentState = this.states[state]
this.game.speed = this.game.maxSpeed * speed
this.currentState.enter()
}
}
playerStates.js
const states = {
SITTING: 0,
RUNNING: 1,
JUMPING: 2,
FALLING: 3
}
class State {
constructor(state) {
this.state = state
}
}
export class Sitting extends State {
constructor(player) {
super('SITTING')
this.player = player
}
enter() {
this.player.frameX = 0
this.player.maxFrame = 4
this.player.frameY = 5
}
handleInput(input) {
if (input.includes('ArrowLeft') || input.includes('ArrowRight')) {
this.player.setState(states.RUNNING, 1)
}
}
}
export class Running extends State {
constructor(player) {
super('RUNNING')
this.player = player
}
enter() {
this.player.frameX = 0
this.player.maxFrame = 8
this.player.frameY = 3
}
handleInput(input) {
if (input.includes('ArrowDown')) {
this.player.setState(states.SITTING, 0)
} else if (input.includes('ArrowUp')) {
this.player.setState(states.JUMPING, 1)
}
}
}
export class Jumping extends State {
constructor(player) {
super('JUMPING')
this.player = player
}
enter() {
if (this.player.onGround()) this.player.vy -= 27
this.player.frameX = 0
this.player.maxFrame = 6
this.player.frameY = 1
}
handleInput(input) {
if (this.player.vy > this.player.weight) {
this.player.setState(states.FALLING, 1)
}
}
}
export class Falling extends State {
constructor(player) {
super('FALLING')
this.player = player
}
enter() {
this.player.frameX = 0
this.player.maxFrame = 6
this.player.frameY = 2
}
handleInput(input) {
if (this.player.onGround()) {
this.player.setState(states.RUNNING, 1)
}
}
}
script.js
import { Player } from './player.js'
import { InputHandler } from './input.js'
import { Background } from './background.js'
window.addEventListener('load', function() {
const canvas = document.getElementById('canvas1')
const ctx = canvas.getContext('2d')
canvas.width = 500
canvas.height = 500
class Game {
constructor(width, height) {
this.width = width
this.height = height
this.groundMargin = 80
this.speed = 0
this.maxSpeed = 4
this.background = new Background(this)
this.player = new Player(this)
this.input = new InputHandler()
}
update(deltaTime) {
this.background.update()
this.player.update(this.input.keys, deltaTime)
}
draw(context) {
this.background.draw(context)
this.player.draw(context)
}
}
const game = new Game(canvas.width, canvas.height)
console.log(game)
let lastTime = 0
function animate(timeStamp) {
const deltaTime = timeStamp - lastTime
lastTime = timeStamp
ctx.clearRect(0, 0, canvas.width, canvas.height)
game.update(deltaTime)
game.draw(ctx)
requestAnimationFrame(animate)
}
animate(0)
})
This is my next challenge. Bring it on!