Skip to main content

News

Topic: Proof of Concept: Chrono Trigger-style text boxes. (Read 3826 times) previous topic - next topic

  • Mooch
  • [*][*][*]
Proof of Concept: Chrono Trigger-style text boxes.
Muhu. Muhuhaha. UWAHAHAHAHAHAHA!!

Back before the site nuke, back when I first got into Sphere something like two years ago, I wanted to make Chrono Trigger-style text boxes, where the text prints out like a typewriter, but you can walk around while it does so.

A newbie, I tried using a for loop. Naturally, it prevented movement until the text had finished printing.

Fast forward a year. I start messing around with Canvas. The CT text problem haunts me. I decide to create a function that emulates the functionality of a for loop, without locking up the system. I succeed.

And now, I've converted it to Sphere.

First of all, here's the code. I warn you, this is "just works" code. Don't blame me if it's so messy and inefficient that you vomit.

main.js
Code: [Select]
var font = GetSystemFont();

var LoopPlease = 0;
var LoopCount = -1;
var LoopEnd = 0;
var CurrentText = "";

function LoopChecker(){
  switch (true){
  case (LoopPlease == 0):
  break;
  case (LoopPlease == 1 && LoopCount > LoopEnd):
  LoopPlease = 0;
  break;
  case (LoopPlease == 1 && LoopCount < 0):
  LoopCount = 0;
  case (LoopPlease == 1 && LoopCount <= LoopEnd):
  LoopCount ++;
  LoopThis();
  break;
  }
}

function FiveLoop(ThisFunction,ThisManyTimes){
  LoopPlease = 1;
  LoopThis = ThisFunction;
  if (LoopEnd = 0){
  LoopEnd = ThisManyTimes;
  }
  else{}
}

function TypeWrite(){
  LoopEnd = CurrentText.length;
  PrintThis = CurrentText.substr(0,LoopCount);
  font.drawTextBox(0,0,256,128,0,PrintThis);
}

function game() {
CreatePerson("player1", "spWar.rss", false);
AttachInput("player1");
SetRenderScript("LoopChecker()");
  MapEngine("test_map.rmp", 60);
}


In test_map.rmp, an Entity's On Activate (Touch)
Code: [Select]
CurrentText = "Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies.";
FiveLoop(TypeWrite);


Things I Already Know...
1) The text disappears the second it finishes displaying. That's fine, this is just a proof of concept, to show that you can have a character walk around while text is printing. Displaying the full text for a given amount of time or until the player gives input can be added later.

2) There's no way to modulate the speed at which the text displays. Well, other than to change the framerate of the Map Engine. Again, this can be dealt with later.

3) The FiveLoop function is only capable of passing argumentless functions stored in variables.

Questions I Have...
1) Why is the FiveLoop function only capable of passing argumentless functions stored in variables? If you were to change TypeWrite to...

Code: [Select]
function TypeWrite(SomeText){
  LoopEnd = SomeText.length;
  PrintThis = SomeText.substr(0,LoopCount);
  font.drawTextBox(0,0,256,128,0,PrintThis);
}


And changed the On Touch entity's code to...
Code: [Select]
FiveLoop(TypeWrite("Hello, world!"));


The game crashes when you touch the entity, complaining...
"LoopThis is not a function."

Or, alternatively, if you add this to main.js...
Code: [Select]
var SayHello = TypeWrite("Hello, world!");


And have the entity's code as...
Code: [Select]
FiveLoop(SayHello);


...you get the same error. LoopThis is not a function.

Even if you just add empty parenthesis to the otherwise-working entity code...
Code: [Select]
CurrentText = "Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies.";
FiveLoop(TypeWrite());


...it complains that LoopThis is not a function.

Why do all those things churn out the same error?

2) If I put the entity's code on On Activate (Talk) instead of touch, it doesn't work. No idea why; I've got input attached and I tried pressing all four of Sphere's buttons -- A, B, X and Y.

3) When everything's working fine and I touch the entity and it types the text and I can walk around while it does it, if I wait until it's finished and then touch the entity again, it won't print the text again. Why? (Also, if there's a second entity with a different CurrentText trying to FiveLoop, only the first one I touch will work, then neither will work after that.)

4) Notice that none of my code uses FlipScreen();
So then why does it work? Normally, if you try to draw text without using FlipScreen(), nothing happens. Is it because it's running in SetRenderScript, so that's automatically flipping it?

Thanks so much to anyone who can help me figure this stuff out ^_^

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #1
I find it really very strange you wrote that code for something like a typewriting text box. This piece of code should do nothing:

Code: (javascript) [Select]

function FiveLoop(ThisFunction,ThisManyTimes){
  LoopPlease = 1;
  LoopThis = ThisFunction;
  if (LoopEnd = 0){
  LoopEnd = ThisManyTimes;
  }
  else{}
}


You have an else clause that has nothing in it. You have LoopPlease set to 1 which seems to do nothing if it weren't for it being a global variable. You set LoopEnd to 0 directly rather than comparing it to 0, so in fact that whole if clause does nothing. It only sets LoopPlease to 1, which I guess sets the whole thing off.

This is the strangest:
Code: (javascript) [Select]

  switch (true){
  case (LoopPlease == 0):
  break;
  case (LoopPlease == 1 && LoopCount > LoopEnd):
  LoopPlease = 0;
  break;
  case (LoopPlease == 1 && LoopCount < 0):
  LoopCount = 0;
  case (LoopPlease == 1 && LoopCount <= LoopEnd):
  LoopCount ++;
  LoopThis();
  break;
  }


You don't know what a switch statement does, do you? I didn't think this would work... I think the last case is the only case being called, since it is the only one to be true forever. In this case the switch is just a true/false switch... TBH I never knew you could do that with switches - which is why your code "works".

Here is what switches really do. They take a variable up top, and splits it 'n' ways:

Code: (javascript) [Select]

var x = 1;
switch (x) {
    case 0: do_x(); break;
    case 1: do_y(); break; // this get's called.
    //...
}


It is equivalent to writing:
Code: (javascript) [Select]

var x = 1;
if (x == 0) { do_x(); }
if (x == 1) { do_y(); }  // this get's called.
//...


Indeed in JS you may almost get by without switches; they are just a stylistic method of handling things that would take many if conditions, and perhaps faster to execute depending on the circumstance. A switch can also compare strings, just like you can in an if. A switch on boolean values would only do one of two things and is usually useless to do, an if would suffice.

The LoopThis is not a function error has to do with the LoopThis(); code in your switch. It is calling a function. In FiveLoop you set LoopThis to LoopFunction. So it only runs the function you give it. That is why this works: FiveLoop(TypeWrite);

These things I pointed out is why you have errors. Here, I'll show you a more or less correct proof of concept that doesn't do weird things. In this way I hope you can learn something more about JS and Sphere. :)

Code: (javascript) [Select]

function TextTyper()
{
    this.msg = "";
    this.loc = 0;
    this.wind = GetSystemWindowStyle();
    this.font = GetSystemFont();
    this.time = 0;
}

TextTyper.prototype.type = function(msg) {
    this.msg = msg;
    this.loc = 0;
    this.time = GetTime();
}

TextTyper.prototype.update = function() {
    this.wind.drawWindow(16, 16, 256, 128);
    this.font.drawTextBox(16, 16, 256, 128, 0, this.msg.substr(0, this.loc));

if (this.time + 25 < GetTime()) {
if (this.loc < this.msg.length) this.loc++;
this.time = GetTime();
}
}

var typer = new TextTyper();

function game()
{
    typer.type("Hello, how are you doing? This is a fine day, have fun!");
    SetRenderScript("typer.update();");
    MapEngine("test.rmp", 60);
}


Ok, now how does it draw without stopping the player? It uses the RenderScript. You don't need to call FlipScreen for a RenderScript, the MapEngine does that for you anyways. Only when you are ever outside of the MapEngine do you have to flip the screen to draw stuff. It also has no while or for loops, which we call "blocking code", because it blocks the map engine. My and your code both increment a counter in real-time. I however added a timer to to so it increments slower, otherwise the map engine - being ran at 60 fps - prints the thing pretty fast.
  • Last Edit: December 10, 2013, 02:42:40 pm 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

  • Mooch
  • [*][*][*]
Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #2
I told you my code was awful. I warned you. You were warned :p

Lemme explain what I was doing, though, just to elucidate you on the origins of my inexperienced coding.

First of all, I should note that my FiveLoop is meant to be a general-purpose non-blocking loop function; I can throw any function and a number at it and have it loop that many times without locking up the system.

So, LoopChecker constantly runs, seeing if LoopPlease is 0 or 1. If it's 0, LoopChecker does nothing. If it's 1, it runs a specific function (set by FiveLoop) a given number of times (up to LoopEnd), using LoopCount as an interator (like "i = whatever" in for-loops -- also get it? For-loop? Five Loop?).

A given script, be it in a Trigger or Entity or wherever, can give a function and a number to FiveLoop, and it'll activate LoopChecker and repeat that function as many times as indicated.

FiveLoop sets which function to loop with LoopThis, and turns the LoopChecker on by changing LoopPlease to 1. The "if LoopEnd" thing was written that way so that if the function calling FiveLoop didn't give a default LoopEnd value, it would set one, but otherwise it would leave it alone.

I dunno what you mean by it should do nothing, but it does. If you run that code and comment-out everything but LoopPlease = 1 in FiveLoop, the code won't work; nothing will loop, nothing will happen.

I actually do know how switch cases work, I just had to hack it to get it to do what I wanted. Normally, yes, you pass a value to check into the switch, however, if you pass "true" into a switch, you can have complex cases that check multiple things at once. You can't do, for example...

Code: [Select]
var x = 1;
var y = 2;
switch (x) {
    case 0: do_alpha(); break;
    case 1 && y == 0: do_beta(); break;
    case 1 && y > 0: do_delta(); break;
}


Passing "true" to a switch case is the only way to have complex comparisons. Seems odd, because it's not the intended use of a switch case, but it does work; my code doesn't just run the last thing, otherwise it wouldn't work at all. And it does.

Quote
The LoopThis is not a function error has to do with the LoopThis(); code in your switch. It is calling a function. In FiveLoop you set LoopThis to LoopFunction. So it only runs the function you give it. That is why this works: FiveLoop(TypeWrite);


But what I don't understand is why, if I pass a function with arguments through FiveLoop, it errors. I know it only runs the function I give it, but why is such a limitation placed on the functions I can give it? Why can't I have TypeWrite be given a string in code ("TypeWrite(string)") and then do FiveLoop(TypeWrite("Hello, world!"));


Here, I'll show you a more or less correct proof of concept that doesn't do weird things. In this way I hope you can learn something more about JS and Sphere. :)

Code: (javascript) [Select]

function TextTyper()
{
    this.msg = "";
    this.loc = 0;
    this.wind = GetSystemWindowStyle();
    this.font = GetSystemFont();
    this.time = 0;
}

TextTyper.prototype.type = function(msg) {
    this.msg = msg;
    this.loc = 0;
    this.time = GetTime();
}

TextTyper.prototype.update = function() {
    this.wind.drawWindow(16, 16, 256, 128);
    this.font.drawTextBox(16, 16, 256, 128, 0, this.msg.substr(0, this.loc));

if (this.time + 25 < GetTime()) {
if (this.loc < this.msg.length) this.loc++;
this.time = GetTime();
}
}

var typer = new TextTyper();

function game()
{
    typer.type("Hello, how are you doing? This is a fine day, have fun!");
    SetRenderScript("typer.update();");
    MapEngine("test.rmp", 60);
}


Somehow, I always feel guilty when you write me out a huge block of code. Guilty that I'm taking up your time that could be spent on Sphere-SFML and other things. I'll take it, though, heh.

This is OOP, right? I've never done objects before. Never used prototype, never actually used "this." before. It'll take me awhile, but I'm gonna figure out precisely how this code works.

Two things, though...
1: Your code constantly draws the window on the screen; it never goes away. The text types just fine (and indeed, if I put typer.type on an entity's On Touch and touch the entity multiple times, it'll re-type the text from the beginning). As I'm reading the code and figuring it out, I'll see if I can figure out why it's doing this. I'll only ask you to explain it if I totally fail. Not sure if you knew this was happening, though, which is why I bring it up.

(1.5: Also, once the text finishes typing out, it just stays displayed forever, it never clears. Again, I'll find a way to add that in, though.)

2: As I said at the beginning, my FiveLoop was intended to be general purpose. I forgot to mention that in the original post. Even I knew that I could've done away with a lot of the code if I were just writing a typewriting text box, but I wanted to be able to pass any arbitrary function into FiveLoop to have it loop.

Once I learn all I can from your sample code, I'll see if I can write a general-purpose non-blocking looper. Though I suspect it'll have the same problem of only being able to work with argument-less functions stored in variables; I think that's a problem with Javascript, not any given code.

I can't thank you enough, Rad ^_^

Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #3

Indeed in JS you may almost get by without switches; they are just a stylistic method of handling things that would take many if conditions, and perhaps faster to execute depending on the circumstance.


They are also nice for grouping multiple conditions without nested boolean comparisons in `if` statements.

And I have noticed some rather odd performance characteristics of switches, particularly with rearranging cases, in Sphere's Spidermonkey.


But what I don't understand is why, if I pass a function with arguments through FiveLoop, it errors. I know it only runs the function I give it, but why is such a limitation placed on the functions I can give it? Why can't I have TypeWrite be given a string in code ("TypeWrite(string)") and then do FiveLoop(TypeWrite("Hello, world!"));


Wait, what exactly are you trying to do here? If you are trying to use callbacks, you probably will want to use anonymous functions.

Code: [Select]

function DoThings(f){
  f();
}

//Let's say I want to draw some text...

DoThings(
  function(){
    GetSystemFont().drawText(16, 16, "Words!");
  }
);

//Or, I think this is exactly what you want...
FiveLoop(function(){TypeWriter("Hello, World!");});



That's a simple way to pass a function and associated arguments as a callback. It constructs a function inline, and gives that as an argument. Very useful for callbacks when you need variable numbers or action-specific arguments.

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #4
Quote from: mooch

I actually do know how switch cases work, I just had to hack it to get it to do what I wanted. Normally, yes, you pass a value to check into the switch, however, if you pass "true" into a switch, you can have complex cases that check multiple things at once


I don't agree with the switch stuff from a purely syntactical point of view: I'd rather use if statements, since that's exactly what you were wanting to do out of it. And no, your switch does not work as you think it does since the only two values from a condition is either true or false, so only two things of that switch ever get executed, which two is beyond me, and why your code works is beyond me and I've been programming JS for near a decade now. I'm not angry or anything, I'm just confused by the code that's all.

If you wanted a five-loop to do things over time, well, you can do something like this:
Code: (javascript) [Select]

function Looper(times, func)
{
    this.times = times;
    this.func = func;
}

Looper.prototype.update = function() {
    if (this.times > 0) {
        this.func();
        this.times--;
    }
}

Looper.prototype.isFinished = function() { return this.times == 0; }

var txtlen = text.length;
function DecrementText() {
    txtlen--;
}

var fiveloop = new Looper(5, Decrement);

function game() {
   SetRenderScript("fiveloop.update();");
   MapEngine("map.rmp", 60);
}


I know I'm writing code for you, but my intention is for you to try and learn from it. Really! Sometimes your first pass is good, but then once you see someone elses code you get better. I had to show you code since you are destroying your own knowledge of JS by using switches in unintended ways, and that might be an issue later on down the road.

So, OOP.

In JS all functions are objects.
Code: (javascript) [Select]

function MyObject(a_size) {
    this.size = a_size;
    this.weight = 20;
}

MyObject.prototype.print = function() {
    Abort("My size is: "+ this.size + ", and weight: " + this.weight + ".");
}

var obj1 = new MyObject(15);
obj1.print(); // says it has size 15, weight 20.

var obj2 = new MyObject(5);
obj2.print(); // says it has size 5, weight 20.


The 'this' pointer refers to the current object in scope. Most of the time it is the direct object you are working with. By using 'this' you can store variables into the object to persist them for use later on. This is good in RPG games since you have many stats you want to keep around, like player stats and enemy stats.

The 'prototype' keyword is uesd to attach a method or property onto the object at a more fundamental level. By using prototype to attach a function to an object you create a method - a function that does work with respect to the object it is attached to. Notice in 'print' I used 'this'? That means I can recall the variable stored in the object and use it. Notice in the code above obj1 and obj2 print out different results? That's because the size property of each has changed.

This was useful in my typing demo to store the message and how far along it has typed out. You were doing this too, albeit in the global scope using global variables, which is not bad for first time (easily the easiest way to do it). But then you start having too many variables in the global scope which might be a bad thing later on. Object contain the variables in their own self, so two objects can have the same property name, but different values.

If you do this:
Code: (javascript) [Select]

function MyObject(a_size) {
    var size = a_size;
    this.weight = 20;
}


The size variables would be destroyed at the termination of the function. The 'this' value persists it within the object.
  • Last Edit: December 10, 2013, 05:37:50 pm 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

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #5

I told you my code was awful. I warned you. You were warned :p


So close, but no cigar.  How you say that is "I warned you about code bro! I told you dog!" :D

...yeah, no, I couldn't resist. :p
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Mooch
  • [*][*][*]
Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #6

But what I don't understand is why, if I pass a function with arguments through FiveLoop, it errors. I know it only runs the function I give it, but why is such a limitation placed on the functions I can give it? Why can't I have TypeWrite be given a string in code ("TypeWrite(string)") and then do FiveLoop(TypeWrite("Hello, world!"));


Wait, what exactly are you trying to do here? If you are trying to use callbacks, you probably will want to use anonymous functions.

Code: [Select]

function DoThings(f){
  f();
}

//Let's say I want to draw some text...

DoThings(
  function(){
    GetSystemFont().drawText(16, 16, "Words!");
  }
);

//Or, I think this is exactly what you want...
FiveLoop(function(){TypeWriter("Hello, World!");});



That's a simple way to pass a function and associated arguments as a callback. It constructs a function inline, and gives that as an argument. Very useful for callbacks when you need variable numbers or action-specific arguments.


The great advantage to learning by doing, rather than by reading theory, is that you can do things much more quickly than someone learning by theory. For example, me and a friend of mine both got into GameMaker at the same time (~2006). He chose to read through introductory tutorials, I chose to dive headfirst in. Within a week I had churned out a simple Tamagotchi-esque virtual pet; he scarsely knew how to print text at that point.

The disadvantage -- that speed comes at the cost of a foundational understanding of the language itself. Case in point? I've never heard the terms "callback" or "anonymous function" until just now. I had been calling "callbacks" "nested functions." Also, I had no idea how to do that thing you just showed me; I didn't know that was possible.

But I get it now! Sort of. I don't really understand how that works, like how Javascript processes what you just typed up there, but I do understand that it works, and I think I can replicate it for my own purposes.

And yes, that is exactly, precisely what I want! Thanks ^_^


Quote from: mooch

I actually do know how switch cases work, I just had to hack it to get it to do what I wanted. Normally, yes, you pass a value to check into the switch, however, if you pass "true" into a switch, you can have complex cases that check multiple things at once


I don't agree with the switch stuff from a purely syntactical point of view: I'd rather use if statements, since that's exactly what you were wanting to do out of it.


Indeed, that is what I wanted out of it. Back when I was using GameMaker, I would often use long sequences of if statements, and at some point on the GM forums, I was asking for help with some code and someone introduced me to switch cases.

Visually, they're much more appealing and easier to read than tons of if statements, so I've been using them for when I have more than two or three ifs ever since. Didn't know it was bad coding practice.

And no, your switch does not work as you think it does since the only two values from a condition is either true or false, so only two things of that switch ever get executed, which two is beyond me, and why your code works is beyond me and I've been programming JS for near a decade now.


Lol! I'll have to tinker around with my code, then, and figure out exactly what's going on, and how and why it works. What a mystery :)

I'm not angry or anything, I'm just confused by the code that's all.


I have avatars turned off. In my head, I picture you as a thirty-something nerd sitting in the lotus position on a rock at the top of a mountain, a 3G netbook at your side, across which you occasionally pass your hand, leaving code and coding wisdom in its wake.

I don't picture you as a creature capable of anger :p

If you wanted a five-loop to do things over time, well, you can do something like this:
Code: (javascript) [Select]

function Looper(times, func)
{
    this.times = times;
    this.func = func;
}

Looper.prototype.update = function() {
    if (this.times > 0) {
        this.func();
        this.times--;
    }
}

Looper.prototype.isFinished = function() { return this.times == 0; }

var txtlen = text.length;
function DecrementText() {
    txtlen--;
}

var fiveloop = new Looper(5, Decrement);

function game() {
   SetRenderScript("fiveloop.update();");
   MapEngine("map.rmp", 60);
}


I know I'm writing code for you, but my intention is for you to try and learn from it. Really! Sometimes your first pass is good, but then once you see someone elses code you get better. I had to show you code since you are destroying your own knowledge of JS by using switches in unintended ways, and that might be an issue later on down the road.


I only feel guilty 'cause I feel like your time is valuable and it's being wasted on me. If I posted an outline for a small game I wanted to make and you coded the whole thing for me, I'd be thrilled. 'cause then of course I could use it not only to learn how to do what I wanted to do, but I could also just take it and modify it and add new features to the game.

Heh, though it is pretty funny -- the first project I end up churning out in Sphere is going to be chiefly your code :p

So, OOP.

In JS all functions are objects.


That's the kind of thing I'd probably know if I'd stuck with that Javascript book I tried reading and returned to the library a week later 'cause I couldn't understand it.

Code: (javascript) [Select]

function MyObject(a_size) {
    this.size = a_size;
    this.weight = 20;
}

MyObject.prototype.print = function() {
    Abort("My size is: "+ this.size + ", and weight: " + this.weight + ".");
}

var obj1 = new MyObject(15);
obj1.print(); // says it has size 15, weight 20.

var obj2 = new MyObject(5);
obj2.print(); // says it has size 5, weight 20.


Okay, now that I understand. I googled the JS reference prototype thing...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fprototype

...and I have no friggin' idea what any of that means. There is a nonzero but infinitely small chance I could've read that JS reference page, and then churned out the code you just wrote.

But looking at your code, I see how it works.

The 'this' pointer refers to the current object in scope. Most of the time it is the direct object you are working with. By using 'this' you can store variables into the object to persist them for use later on. This is good in RPG games since you have many stats you want to keep around, like player stats and enemy stats.


Oh yeah, I actually did use 'this' before. I think it was last time I was working with Sphere. I made a player object (I guess -- I didn't really know it was an "object") and did this.hp, this.str, this.exp, etc.

The 'prototype' keyword is uesd to attach a method or property onto the object at a more fundamental level. By using prototype to attach a function to an object you create a method - a function that does work with respect to the object it is attached to.


There's another point of confusion I had. Why are they called "methods?" That's completely nondescript. If I were inventing programming, I'd've called them "object functions" or something, 'cause that's what they are.

Notice in 'print' I used 'this'? That means I can recall the variable stored in the object and use it. Notice in the code above obj1 and obj2 print out different results? That's because the size property of each has changed.

This was useful in my typing demo to store the message and how far along it has typed out. You were doing this too, albeit in the global scope using global variables, which is not bad for first time (easily the easiest way to do it). But then you start having too many variables in the global scope which might be a bad thing later on. Object contain the variables in their own self, so two objects can have the same property name, but different values.


Which would be useful for me because when I sit down to code something, I think of my program as a set of variables and the functions that manipulate them, but since I've always excluslvely used global variables ('cause I didn't know any other thing), I'd have to give them shorthand names. player1_hp, player2_exp, etc. which gets cumbersome to write.

If you do this:
Code: (javascript) [Select]

function MyObject(a_size) {
    var size = a_size;
    this.weight = 20;
}


The size variables would be destroyed at the termination of the function. The 'this' value persists it within the object.


Might it sometimes (probably rarely if ever) be advantageous to have variables destroyed when a function is done running? Especially if working with older tech where resources are limited, it might sometimes be better to have them destroyed so they don't linger around, eating up RAM.



I told you my code was awful. I warned you. You were warned :p


So close, but no cigar.  How you say that is "I warned you about code bro! I told you dog!" :D

...yeah, no, I couldn't resist. :p


Aww, five points from Gryffindor. Keep that up and you'll lose the House Cup, mister.

~ ~ ~

Next post I'm gonna go through your typewriter code, Radnen, and figure out how it works line-by-line. It'll be fun for me to post about it as I go through it, and maybe elucidating for everyone else as to how a novice's mind works.

Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #7

If you do this:
Code: (javascript) [Select]

function MyObject(a_size) {
    var size = a_size;
    this.weight = 20;
}


The size variables would be destroyed at the termination of the function. The 'this' value persists it within the object.


Might it sometimes (probably rarely if ever) be advantageous to have variables destroyed when a function is done running? Especially if working with older tech where resources are limited, it might sometimes be better to have them destroyed so they don't linger around, eating up RAM.


No, not manually. JavaScript is garbage-collected, so you let the JS engine do it for you. You only need to worry about scoping, and you can trust the engine to clear out memory. When it needs more memory, it looks for handles (objects and functions) that have no valid references (and so cannot be accessed ever again), and frees them. All automagically.

  • Mooch
  • [*][*][*]
Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #8
Alright! Here we go...

Code: (javascript) [Select]

function TextTyper()
{
    this.msg = "";
    this.loc = 0;
    this.wind = GetSystemWindowStyle();
    this.font = GetSystemFont();
    this.time = 0;
}


Alright, all pretty obvious and self-explanatory. And checking the Wiki's OOP primer, I can see that this actually isn't creating a function, despite JS's wording. This is actually a prototype. Which, actually, is a term I don't prefer. I prefer class, for some reason. Archetype would be better than both, but prototype sounds like a primitive version of something, whereas archetype sounds like the template for something, which is what a class/prototype is.

Code: (javascript) [Select]
TextTyper.prototype.type = function(msg) {
    this.msg = msg;
    this.loc = 0;
    this.time = GetTime();
}


Okay, so this uses the prototype dot operator thingy to create a method called type for TextTyper. Interesting syntax -- Object.prototype.method = function(stuff). I'm used to "function nameOfFunction(stuff) {do this}." Seeing "function(stuff)," like just using the generic term "function" instead of a name, is kinda weird, but I get it.

Now wait. We obviously set local(?) variables here (I've only ever heard the term "global variable" so I presume the opposite is called local), but they're the same as in the TextTyper class declaration above. So are these different? Is TextTyper.msg different from TextTyper.type.msg? If so, why did we have to declare those properties in TextTyper? If not, why do we have to declare them here?

Either way, I find it interesting that all TextTyper.type does is set a message, location and time. Even though we use TextTyper.type("whatever") later to print whatever, TextTyper.type isn't where we have our actual typing-out code.

Code: (javascript) [Select]
TextTyper.prototype.update = function() {
    this.wind.drawWindow(16, 16, 256, 128);
    this.font.drawTextBox(16, 16, 256, 128, 0, this.msg.substr(0, this.loc));


Okay see, now I'm confused. Because we gave TextTyper a msg, loc and time using 'this.', and then TextTyper.type had a repeat of those. But here, TextTyper.update just uses TextTyper's wind and font. So why was it necessary to re-do .msg, .loc and .time in TextTyper.type but not re-do .wind and .draw here?

Oh, but I see why this code constantly draws a window on the screen -- TextTyper.update is constantly running via SetRenderScript, and here, it's constantly drawing a window. Technically it's also constantly drawing text to that window, but if msg = "", no text will be drawn.

Code: (javascript) [Select]

if (this.time + 25 < GetTime()) {
if (this.loc < this.msg.length) this.loc++;
this.time = GetTime();
}
}


I'm uncomfortable with nested ifs (just 'cause I've never done them myself), but I'll try to marshall through. Okay, TextTyper.type GetTime()'d for us, so when this piece of code first starts, this.time will equal...whatever time Sphere is counting forward from. The exact number doesn't matter.

"if (this.time + 25 < GetTime())"

Okay, so presuming SetRenderScript is running our code once per millisecond, when the code very first gets here, this.time --

OHH! I get it! In the initial TextTyper object declaration at the beginning, we were initializing the local variables. Then TextTyper.type modifies them. TextTyper.update doesn't need to mess with .wind or .draw because they don't need modified! Sorry; writing in a stream of consciousness here. Where was I?

Yeah, when the code very first gets to "if (this.time + 25 < GetTime())," the new GetTime() here will only be like one millisecond past this.time. Adding that +25 delays the code inside the if by 25 milliseconds so it doesn't print as fast.

Hmm. Here's something I don't understand, though. The GetTime() here isn't being stored in a variable. Every time TextTyper.update runs, it should be Getting a new Time...which should always be just one millisecond after this.time. Oh, wait, no. I just ran a test in Sphere -- GetTime constantly counts upward. Nevermind.

Anyway, when I write up my own text typer, I'll make the +25 a variable. this.txtspd, for instance, so that the speed can be modulated by the end-user. Heh, this offers some insight into videogames I've played that let you alter the text writing speed, 1-9 or 1-100 or something, and higher is typically slower. That's counter-intuitive, but makes sense; the higher this.txtspd, the more a millisecond delay and the slower the text types. Since most people who play videogames aren't coders, though, they probably should've flipped the visual representation of the text speed option slider, so it'd make sense to msot people (higher = faster).

Oh, and the inside of the first if is pretty self-explanatory -- it prints msg from 0 to loc, then increments loc, and Gets a new Time in this.time.

Code: (javascript) [Select]
var typer = new TextTyper();

function game()
{
    typer.type("Hello, how are you doing? This is a fine day, have fun!");
    SetRenderScript("typer.update();");
    MapEngine("test.rmp", 60);
}


Oh. That's it.

Wow, that was pretty easy to understand (my little hiccup with variable declarations notwithstanding).

Hmm again. Something I don't understand, though maybe I do. Why do we have to make a new TextTyper and call it typer? Why can't we just do TextTyper.type("Hello, how are you doing? This is a fine day, have fun!");?

I mean, in a general sense, I can understand doing this. If we had a class for PlayerCharacters, for instance, obviously you'd "player1 = new PlayerCharacter; player2 = new PlayerCharacter;" etc.

I suppose I don't understand why TextTyper has to be a class. Or rather, why we have to make an instance of it, why we can't just use it itself. Maybe if an instance could have a different window color or text speed or what have you. But no, those could be modulated with variables. So yeah, I kinda don't get why this has to be done as an object. Other than the fact that you can't append methods to regular functions.

Which brings me to a simple question. Does everything need to be done with classes and objects and such? Like, using my Gauntlet-esque game as an example, consider treasure. All it does is, if you step on it, it plays a sound effect, adds to your Score, and disappears. So, the code might be...

Code: (javascript) [Select]
function TreasureBox(); {
LoudSound(treasureget.midi);
Sound.play(false); // Not sure if this is how Sounds work, but you get the idea.
PlayerScore += 100;
DeleteSelf(); // Or whatever. I dunno how to erase entities.
}


And then in the treasure entity's On Activate Touch, just call TreasureBox();

I can't think of any way or reason to involve objects here. Unless you were to have different kinds of treasure that give different Score values, then obviously a simple object would be nice, 'cause then...wait, no. You could just modify my above code to...

Code: (javascript) [Select]
function TreasureBox(amount); {
LoudSound(treasureget.midi);
Sound.play(false);
PlayerScore += amount;
DeleteSelf();
}


Yeah. So I can't see any reason to do this as an object/class. Other than to avoid having any raw functions in your code, to have everything be a class. Is there some reason to do the above treasure code in an OOP way? Would it be more efficient?

I learned a lot from your code example, though, Radnen. Thanks again. Next post, I'll try cooking up some simple OOP myself.

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Proof of Concept: Chrono Trigger-style text boxes.
Reply #9
For simple games you don't need complex objects, a few will go a long ways though. I actually use a mix of pure functions and   pure objects. in fact, objects are only an organizational tool. your analysis of my code was spot on.  though I will say a local variable dies at the end of a function call, just like global variables dying at the end of your game. by dying I mean unusable. the this reference will make sure you recall that property even if you a are no longer in scope. heck, even at the end of your program you can still recall  those properties if you can still find it in your ram, this is now hackers do their job.
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