Skip to main content

News

Topic: Blitting trouble with scrolling stars background. (Read 7456 times) previous topic - next topic

0 Members and 1 Guest are viewing this topic.
  • Mooch
  • [*][*][*]
Blitting trouble with scrolling stars background.
Lally-ho, everyone! You know the schpeil by now -- symptoms abating, getting back into Sphere, need some help, etc.

I'm trying to create an ultra-simple, bare-bones space shooter, just to get back into the swing of things, and I've hit a snag. I'm starting off just trying to make stars slowly, randomly scroll in the background, from the top of the screen to the bottom.

I think I've got the basic idea (although you'll be a better judge of that than I) but I can't figure out how to work Sphere's image blitting because what I'm doing is, using a for-loop to populate an array of star data objects and assign them random X and Y values, which presumably works fine. However, I can't use those X and Y values in image.blit, and when I try to put them into a variable, I get an error.

Here's my code. I took a look at FBN's puffpuff game's intro screen's starfield code to get the basic idea of what to do with my own, so I'm using some of his nomenclature.

Code: (Javascript) [Select]
///////////
// SETUP //
///////////
function GameData(){
this.screenWidth = GetScreenWidth();
this.screenHeight = GetScreenHeight();}

var MyGame = new GameData();

///////////////////////
// STARFIELD ROUTINE //
///////////////////////
var StarImg = LoadImage("star.PNG");
// it errors if I do "star.png" for some reason,
// didn't know file extensions were case sensitive O_o

function _Star(x,y){
this.x = x;
this.y = y;}
// the above is straight from FBN's game,
// I presume the leading underscore indicates iterated thingies

function Starfield(){}

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum;
this.speed1 = setSpd1;
this.speed2 = setSpd2;
this.speed3 = setSpd3;

if(!setNum){this.number = 108;}
if(!setSpd1){this.speed1 = 2;}
if(!setSpd2){this.speed2 = 4;}
if(!setSpd3){this.speed3 = 8;} // default values for safety's sake

var myStars = new Array(this.number);

// creates this.number stars at random x/y positions
for(var i=0; i<this.number; i++){
myStars[i] = new _Star(Math.floor(Math.random()*MyGame.screenWidth),
Math.floor(Math.random()*MyGame.screenHeight))
var StarsX = myStars[i].x;
var StarsY = myStars[i].y; // this is the problem code, see below
}
}

Starfield.prototype.exe = function(){
StarImg.blit(StarsX,StarsY); // well technically THIS is the problem code
}

var MyStarfield = new Starfield();

function game(){
MyStarfield.init(108,2,4,8);
while (true){
MyStarfield.exe();}
}


As you can see, this is unfinished code -- the stars aren't moving they're just being drawn to the screen. (Theoretically.) I'm of the mind to get the code to draw static stars before I start animating.

Anyhoo, when I try to run the above, it says StarsX is not defined. I originally had 'var StarsX = myStarsi.x' inside the Starfield.prototype.exe and it told me...

"ssImageBlit - argument 0, invalid integer ... "undefined""

...and I figured since no for-iteration was being done in Starfield.exe, I must just need to move the variable declaration inside of the .init. Obviously, no such luck.

And the only reason I'm bothering with variable declaration of this kind in the first place is because originally, I tried to just do...

Code: (Javascript) [Select]
Starfield.prototype.exe = function(){
StarImg.blit(myStars[i].x,myStars[i].y);
}


And that threw the same "undefined" error in my face.

Any help? I think everything except the blitting works. I think I did the for iteration properly, and the code creates an array of stars with random x and y values. I think it's just that I can't figure out how to work with Sphere's image blitting. But feel free to disabuse me of that notion if my setup is crunked from line 1.

Danke very schoen for any help you can give :]

(edit: fixed code tag highlighting +neo)
  • Last Edit: July 18, 2015, 12:30:04 pm by N E O

  • N E O
  • [*][*][*][*][*]
  • Administrator
  • Senior Administrator
Re: Blitting trouble with scrolling stars background.
Reply #1
Variable scope is your problem here. myStars, i, and StarsX/StarsY are all declared using var within the Starfield.init function, so they're considered undefined elsewhere. Change your code bit so other functions can "see" them, so to speak.

I'm not going to give corrected code for this, since the way I currently code is usually not immediately understandable by other people, and this is one time where I recommend understanding how to do it for future use (because you'll be doing this a lot if you want anything particle-based and/or physics-based, among other things) rather than simply doing it.

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Blitting trouble with scrolling stars background.
Reply #2
I'm with neo on this. To me it seems you have some fundamentals of programming. But to figure out these logic problems the best advice I can give is for you to mentally step through the problem. It does require knowing how JS works though (I'm not talking syntax). How a language works is just as important. Syntax alone can't help you.

For example, variable scoping. You use syntax to create variables, but there are rules to the system. Once you figure out these rules you'll learn to better organize your code.

Here is an instance in which you can use an outer scopes variables:
Code: (javascript) [Select]

var g_x = 0;
var g_y = 0;

function my_func() {
    DrawText(g_x, g_y, "text");
}


Here is an instance in which you can not use an inner scopes variables:
Code: (javascript) [Select]

function my_func() {
    var g_x = 0;
    var g_y = 0;
}

DrawText(g_x, g_y, "text");


Here is an instance in which you can use an object's variables:
Code: (javascript) [Select]

function my_func() {
    this.g_x = 0;
    this.g_y = 0;
}

my_func.prototype.action = function() {
    DrawText(this.g_x, this.g_y, "text");
}

var o = new my_func();
o.action();


Here is an instance in which you can not use an object's variables:
Code: (javascript) [Select]

function my_func() {
    var g_x = 0;
    var g_y = 0;
}

my_func.prototype.action = function() {
    DrawText(this.g_x, this.g_y, "text");
}

var o = new my_func();
o.action();


I won't tell you what the differences are, but I'm sure you can guess by looking at them. You'll notice what's different about each case.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Mooch
  • [*][*][*]
Re: Blitting trouble with scrolling stars background.
Reply #3
Ugh, can't believe I made such a simple mistake. Well, I can actually, it's very characteristic of me. However, I've hit another snag, and this time I'm sure it's not the same rookie scope mistake as last time. Here's the whole revised code, just so you can see it, but I'll highlight the problem part after.

Code: (Javascript) [Select]


///////////
// SETUP //
///////////
function GameData(){
this.screenWidth = GetScreenWidth();
this.screenHeight = GetScreenHeight();}

var MyGame = new GameData();

///////////////////////
// STARFIELD ROUTINE //
///////////////////////
var StarImg = LoadImage("star.PNG");
// it errors if I do "star.png" for some reason,
// didn't know file extensions were case sensitive O_o
// Seriously, filenames are case-sensitive in Sphere? Really?

var myStars = new Array();

function _Star(x,y){
this.x = x;
this.y = y;}
// the above is straight from FBN's game,
// I presume the leading underscore indicates iterated thingies

function Starfield(){
//this.time = 0;
}

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum;
this.speed1 = setSpd1;
this.speed2 = setSpd2;
this.speed3 = setSpd3;

if(!setNum){this.number = 108;}
if(!setSpd1){this.speed1 = 2;}
if(!setSpd2){this.speed2 = 4;}
if(!setSpd3){this.speed3 = 8;} // default values for safety's sake

this.time = GetTime();
var myStars = this.number;
}

Starfield.prototype.pop = function(){
// creates this.number stars at random x/y positions
for(var i=0; i<this.number; i++){
myStars[i] = new _Star(Math.floor(Math.random()*MyGame.screenWidth),
Math.floor(Math.random()*MyGame.screenHeight))
}
}

Starfield.prototype.exe = function(){
this.time = GetTime();

for(var i=0; i<this.number; i++){
StarImg.blit(myStars[i].x,myStars[i].y);
}

if (this.time < GetTime()) {
  myStars[i].y++;
  this.time = GetTime();
}
}

var MyStarfield = new Starfield();

function game(){
MyStarfield.init(32,2,4,8);
MyStarfield.pop();
while(true){MyStarfield.exe();
FlipScreen();}
}


And here's the problem...

Code: (Javascript) [Select]

Starfield.prototype.exe = function(){
this.time = GetTime();

for(var i=0; i<this.number; i++){
StarImg.blit(myStars[i].x,myStars[i].y);
}

/* if (this.time < GetTime()) {
  myStars[i].y++;
  this.time = GetTime();
} */
}


The problem is the commented-out part. If I run it as above, the code works fine, it blits a bunch of random stars on screen, yay me. If I uncomment that code, though, it spits another undefined error at me, saying myStarsI.y is undefined.

But how can that possibly, possibly be the case? Just a few lines above, StarImg.blit clearly uses myStarsI.y!

Further weirdness. If I uncomment the above and run the code, it spits an error at me almost immediately. If I change it to "if (this.time + 10 < GetTime())" it waits a split second longer before erroring. But if I ramp it up to if "(this.time + 100 < GetTime())" it never errors. I sat around for two minutes, wondering if it was somehow interpreting the 100 as 100 seconds, to no avail.

Also, if I jam the movement code in a for loop like so...

Code: (Javascript) [Select]

for(var i=0; i<this.number; i++){
if (this.time < GetTime()) {
  myStars[i].y++;
  this.time = GetTime();
}
}


Something very bizarre occurs. A single random star irregullarly, jerkily creeps downward, but then, randomly, for just a second at a time, it'll randomly move a single other star before continuing with the original star, as if the general of an army is trying to be brave and his troops are slowly, timidly advancing behind him.

I realize I'm just barking my own confusion into the ether at this point and there's not much you can say.

A pertinent question though -- if everything worked perfectly, would putting the star image blitting inside a for-loop lock up the game? Or would it happen so fast that it wouldn't interrupt gameplay? I'm always wary of for-loops for anything other than behind-the-scenes iteration.

If it's any consolation I'll likely scrap this entire page of code and start over from scratch, maybe trying to just animate a singular star exactly the way I want, and then attempting to scale up to multiple stars from there.

Honestly, since this is (supposed to be) a fast-and-dirty "just get back into the swing of things" type deal, I should just manually code variables for each individual star and avoid all this confusing array iteration for-loop stuff. And there's an elephant in the room in that I have no idea how layers work in Sphere, so I dunno what this code is gonna do once the Map Engine is drawing Player and Enemy entities on-screen. I'll have to lollop awkwardly across that bridge when I come to it.

I really ought to make a tutorial when I figure all this stuff out, if for nothing else than my own future use. I forget so much in the long dry periods between being able to code.

  • N E O
  • [*][*][*][*][*]
  • Administrator
  • Senior Administrator
Re: Blitting trouble with scrolling stars background.
Reply #4
The var myStars declaration in Starfield.init is useless, you can safely get rid of that.

Second, variable scope once again plays a role in your code; you can't use myStars[ i ] after the for-loop since i will point to an "out-of-bounds" index.

As for the time comparison, I don't know what's going on there but it kind of looks like you're trying to give the star some velocity. It would be a good idea to look up some game physics tutorials for cleaner ways to do this; Fix Your Timestep and one of CodeIncomplete's JS game dev tutorials are really good starting points, though keep in mind the CodeIncomplete tutorials mainly focus on Web JS.
  • Last Edit: July 19, 2015, 10:25:25 pm by N E O

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Blitting trouble with scrolling stars background.
Reply #5
Hey Mooch, you're getting closer, here's some tips on programming cleaner:

The underscore means nothing, you don't have to use it. It's a personal convention usually indicating a privately scoped function (something very complex that we don't need to get into right now). It's not a syntax thing, it's just a thing.
Code: (javascript) [Select]

function _Star() {
}

// rename it to (make sure you rename it everywhere else too!):
function Star() {
}


Next, you can shorten this area:
Code: (javascript) [Select]

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum;
this.speed1 = setSpd1;
this.speed2 = setSpd2;
this.speed3 = setSpd3;

if(!setNum){this.number = 108;}
if(!setSpd1){this.speed1 = 2;}
if(!setSpd2){this.speed2 = 4;}
if(!setSpd3){this.speed3 = 8;} // default values for safety's sake

this.time = GetTime();
var myStars = this.number; // Like NEO said, this does nothing. Can you guess as to why?
}


Like so:
Code: (javascript) [Select]

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum || 108;
this.speed1 = setSpd1 || 2;
this.speed2 = setSpd2 || 4;
this.speed3 = setSpd3 || 8;

this.time = GetTime();
}


A problem with your iteration, like neo said is that myStars is meaningless here:
Code: (javascript) [Select]

for(var i=0; i<this.number; i++){
if (this.time < GetTime()) {
  myStars[i].y++;
  this.time = GetTime();
}
}


myStars here is treated as an array. But you have to fill that array with values. An array in JS can hold 1 or many objects.

You fill this array in Starfield.prototype.pop(), which makes no sense to me since .pop is a function usually used for removing an item from the bottom of an array.

That stuff can go into Starfield.prototype.init, like so:
Code: (javascript) [Select]

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum || 108;
this.speed1 = setSpd1 || 2;
this.speed2 = setSpd2 || 4;
this.speed3 = setSpd3 || 8;

this.time = GetTime();
this.myStars = new Array(this.number); // this is the correct way to 'save' myStars

// creates this.number stars at random x/y positions
for(var i=0; i<this.number; i++){
  this.myStars[i] = new Star(Math.floor(Math.random()*MyGame.screenWidth),
  Math.floor(Math.random()*MyGame.screenHeight));
}
}


Now, your exe function may look like this:
Code: (javascript) [Select]

Starfield.prototype.exe = function(){
this.time = GetTime();

for(var i=0; i<this.number; i++) {
  StarImg.blit(this.myStars[i].x,this.myStars[i].y);
}

if (this.time < GetTime()) {
  this.myStars[i].y++;
  this.time = GetTime();
}
}


I'm very sorry to have to show you how to code this, but I implore you to compare your stuff with mine, as you can see, there are many scoping issues you still made. I can tell, you never learned from my lesson regarding the property "myStars".

Also do what NEO said and look up basic game physics and the like. Fair warning, you will need to know math. A good programmer can usually visualize the math in their heads. But if you aren't good at math, that's fine too. Use JavaScript as a sandbox to test out math and get an appreciation for it. It's the best I can say. Programming made me a better mathematician than Math courses in school.
  • Last Edit: July 20, 2015, 01:35:02 am by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • N E O
  • [*][*][*][*][*]
  • Administrator
  • Senior Administrator
Re: Blitting trouble with scrolling stars background.
Reply #6

You fill this array in Starfield.prototype.pop(), which makes no sense to me since .pop is a function usually used for removing an item from the bottom of an array.

I presume it's short for populate like exe is short for execute.

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Blitting trouble with scrolling stars background.
Reply #7


You fill this array in Starfield.prototype.pop(), which makes no sense to me since .pop is a function usually used for removing an item from the bottom of an array.

I presume it's short for populate like exe is short for execute.


Still fairly ambiguous. My mind uses the common technical term first, and pop means something different to me. But yeah, you are right, populate it is. I'd just use populate, no need to always shorten one word function calls (unless it's used frequently).
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Mooch
  • [*][*][*]
Re: Blitting trouble with scrolling stars background.
Reply #8

The var myStars declaration in Starfield.init is useless, you can safely get rid of that.


Huh? But the global declaration...
var myStars = new Array();
...only makes myStars an Array, it doesn't fill it with anything. The initialization's...
var myStars = this.number;
...makes the size of myStars whatever size you set it to in Starfield.init, so you can change the number of stars on-the-fly.

Weird, though, you're right -- when I comment it out, nothing changes, the code runs fine. I don't understand -- how does myStars know to be 32 stars in length (thanks to the "MyStarfield.init(32,2,4,8);" call in game()) without that declaration?

Second, variable scope once again plays a role in your code; you can't use myStars[ i ] after the for-loop since i will point to an "out-of-bounds" index.

As for the time comparison, I don't know what's going on there but it kind of looks like you're trying to give the star some velocity. It would be a good idea to look up some game physics tutorials for cleaner ways to do this; Fix Your Timestep and one of CodeIncomplete's JS game dev tutorials are really good starting points, though keep in mind the CodeIncomplete tutorials mainly focus on Web JS.


Ah, nice resources, thanks. Yeah, this is kind of a mess of code. I'm sort of building up towards what I want. Since I ran into problems merely displaying the stars, I got halted. The plan is obviously to have them move, eventually.


Hey Mooch, you're getting closer, here's some tips on programming cleaner:

The underscore means nothing, you don't have to use it. It's a personal convention usually indicating a privately scoped function (something very complex that we don't need to get into right now). It's not a syntax thing, it's just a thing.
Code: (javascript) [Select]

function _Star() {
}

// rename it to (make sure you rename it everywhere else too!):
function Star() {
}


Yeah, I knew it didn't have a syntactic purpose, I meant I thought he was using it as a convention to denote iterated thingies. When basing my code on others code, I typically just use their naming conventions so that I can have an easier time going back and forth from reading their code to modifying mine. I'll rename everything at the end.

Next, you can shorten this area:
Code: (javascript) [Select]

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum;
this.speed1 = setSpd1;
this.speed2 = setSpd2;
this.speed3 = setSpd3;

if(!setNum){this.number = 108;}
if(!setSpd1){this.speed1 = 2;}
if(!setSpd2){this.speed2 = 4;}
if(!setSpd3){this.speed3 = 8;} // default values for safety's sake

this.time = GetTime();
var myStars = this.number; // Like NEO said, this does nothing. Can you guess as to why?
}


Like so:
Code: (javascript) [Select]

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum || 108;
this.speed1 = setSpd1 || 2;
this.speed2 = setSpd2 || 4;
this.speed3 = setSpd3 || 8;

this.time = GetTime();
}


Ahh! Right! I think I knew that at one point, it looks very familiar. Pretty sure I did that on my TextTyper function to provide a default size to the text box if it's not specified. Thanks.

A problem with your iteration, like neo said is that myStars is meaningless here:
Code: (javascript) [Select]

for(var i=0; i<this.number; i++){
if (this.time < GetTime()) {
  myStars[i].y++;
  this.time = GetTime();
}
}


myStars here is treated as an array. But you have to fill that array with values. An array in JS can hold 1 or many objects.


The array is filled elsewhere, with "_Star(x,y)" objects. What that bit of code was intended to do (and realistically, I didn't expect it to be this easy, but I figured I'd try anyway) was, since each _Star in the array had an x and y value, advance the y value of each, thereby creating a star scrolling effect.

But I guess you can't use dot operators on individual elements of an Array the way I tried to there.

You fill this array in Starfield.prototype.pop(), which makes no sense to me since .pop is a function usually used for removing an item from the bottom of an array.


Er, "pop" here is short for "populate." Populate the Starfield. I didn't know that ".pop" was a thing.

That stuff can go into Starfield.prototype.init, like so:
Code: (javascript) [Select]

Starfield.prototype.init = function(setNum,setSpd1,setSpd2,setSpd3){
this.number = setNum || 108;
this.speed1 = setSpd1 || 2;
this.speed2 = setSpd2 || 4;
this.speed3 = setSpd3 || 8;

this.time = GetTime();
this.myStars = new Array(this.number); // this is the correct way to 'save' myStars

// creates this.number stars at random x/y positions
for(var i=0; i<this.number; i++){
  this.myStars[i] = new Star(Math.floor(Math.random()*MyGame.screenWidth),
  Math.floor(Math.random()*MyGame.screenHeight));
}
}


Ah okay, see, I was segregating basically every single function of Starfield into its own prototype because I wasn't 100% sure what could and couldn't be safely combined. I figured, get everything working independently first, then cram it all together.

Also that "this.myStars = new Array(this.number)" is awesome! So much neater than declaring myStars out in the open then manipulating it in .init.

Now, your exe function may look like this:
Code: (javascript) [Select]

Starfield.prototype.exe = function(){
this.time = GetTime();

for(var i=0; i<this.number; i++) {
  StarImg.blit(this.myStars[i].x,this.myStars[i].y);
}

if (this.time < GetTime()) {
  this.myStars[i].y++;
  this.time = GetTime();
}
}


It may indeed, and thanks for cleaning all that up for me. Somehow, though, I suspect that "this.myStarsI.y++;" still won't have the effect of moving the stars I was hoping for, heh. Which is okay, 'cause I'm still trying things, and since I was able to get basic physics working in my platformer, I know I'll be able to get them working here once I get back into the swing of things.

(Can't do "i" in ][ these brackets outside of a code tag or it gets interpreted as itallicize :/)

I'm very sorry to have to show you how to code this, but I implore you to compare your stuff with mine, as you can see, there are many scoping issues you still made. I can tell, you never learned from my lesson regarding the property "myStars".


Don't be! I learn best when I try, fail utterly, then have someone explain to me where I went wrong. You've been a huge help :)

Also do what NEO said and look up basic game physics and the like. Fair warning, you will need to know math. A good programmer can usually visualize the math in their heads. But if you aren't good at math, that's fine too. Use JavaScript as a sandbox to test out math and get an appreciation for it. It's the best I can say. Programming made me a better mathematician than Math courses in school.


I was doing some passable physics when I was working on my simple platformer last year, but I've been away for so long I've forgotten most of it and will have to relearn the hard way.

Thanks for all the help :)

(BTW, still reeeally weirded out by the fact that LoadImage("star.PNG") and LoadImage("star.png") are considered distinct. I've never, ever known file extensions to be case-sensitive, that seems so weird to me.)

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Blitting trouble with scrolling stars background.
Reply #9
Filenames (including the extension, which is really just part of the filename) are case-sensitive on Unix platforms.  On Windows or OSX though, it shouldn't matter.  It's indeed weird if Windows Sphere is treating the two as distinct.

As for this:

Quote
Huh? But the global declaration...
var myStars = new Array();
...only makes myStars an Array, it doesn't fill it with anything. The initialization's...
var myStars = this.number;


If that latter declaration is inside of a function, then you're just declaring a new local variable which shadows the global one.  And that's not how you resize an array anyway, you should have done this:
Code: (javascript) [Select]
myStars.length = this.number;


But in most cases you don't have to do that.  Arrays in JavaScript don't behave they way they do in most other languages.  They will expand dynamically, such that:

Code: (javascript) [Select]
var arr = [];  // shorthand for `new Array()`
arr[999] = 812;
Abort(arr.length);  // will output 1000
  • Last Edit: July 24, 2015, 11:08:48 am by Lord English
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub