Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Objects are Your Friends: How & Why to Use JavaScript Objects for Total Newbies

Greetings, new Twiners!

Objects can help even the newest Twine user. Generally, they are used in Twine to make variables more organized and code more legible. They can also greatly reduce the amount of code.

Let's get right to it.

This is how one creates and object in Twine:
<<set $foo = {}>>
Now, the variable $foo is an object. Who cares, right? ;)

Well, objects can have properties, like so:
<<set $foo= {
property1: "bar",
property2: 894,
property3: $baz,
}>>
More interested now? :)

Imagine you want to create several variables that represent the player. Instead, you can make one object with several properties like this:
<<set $player = {
name: "Jane",
age: 21,
height: 63,
weight: 100,
hair: "Blonde",
eyes: "Blue",
}>>
That's a lot more well organized and legible than seven different variables.

Let's say in the game, Jane gets her hair dyed to brown. To change her hair color, we would do:
<<set $player.hair = "Brown">>
To print the player's new hair color, we would do:
<<print $player.hair>>
That would print "Brown" without the quotes.

That being said, we could do something like this in a Twine passage, using setter-links to change the player's hair color:
Hello, <<print $player.name>>!

What color would you like to dye your hair?

[[Blonde][$player.hair = "Blonde"]]
[[Brown][$player.hair = "Brown"]]
[[Black][$player.hair = "Black"]]
[[Red][$player.hair = "Red"]]
Another thing one can do, cutting out several lines of code in some cases, is set one object to be another.

For example, lets say the player has a gun and that gun is a S&W .38 special snub-nose revolver. We might make an object like this:
<<set $gun = {
name: "S&amp;W .38",
maxAmmo: 6,
ammo: 6,
caliber: 38,
damage: 10,
}>>
Let's say the player finds a new gun, an M1911. Here's how it would compare:
<<set $colt45 = {
name: "Colt .45",
maxAmmo: 8,
ammo: 8,
caliber: 45,
damage: 20,
}>>
Now, here's the easy part. Let's say the player is out of ammo in his revolver and finds the M1911 on a table. The player takes it. Here's how we could replace all the code in the $gun object quickly and easily:
<<set $gun = $colt45>>
You might say every time the player fires a shot, a bullet is used. You could do that like this:
<<set $gun.ammo = $gun.ammo - 1>>
or
<<set $gun.ammo -= 1>>
Those do the exact same thing, the second way is just more concise.

This could come in handy during play:
<<if $gun.ammo gt 0>>
<<set $gun.ammo -= 1>>
Bang! You fire a shot!
<<else>>
You pull the trigger, but hear only a click. You're out of rounds!
<<endif>>
Attached is an example of the material above.

I hope someone finds this useful!

Thanks for reading!

Comments

  • Thanks for this! This is super helpful.
  • Actually, this is super helpful!  Thanks for taking the time to share :)
  • Very nice, Sharpe. I'm gonna rip all the messy variables out of my game and objectify! ;D
  • Hey, glad you guys liked it!

    I'm working up to doing one on lists and how and why to use them next. It should be about as helpful as this one, I think. A lot of Twine users don't realize how important and helpful lists are. Actuallly, the loops guide I wrote isn't real impressive without knowing how to use lists.

    After that, I'll combine the three guides I've writtenloops, objects, and listsinto one and write one more guide; it will be the Big One. It will be about putting objects into lists and sorting/interacting with them, especially with loops (so you can see why I had to write the other three guides first). At that point, your Twine game will look a bit like a "real" computer program. :)
  • This is a fantastic post. Thank you so much for elaborating. :)
  • I've started to use objects too (thanks to your other post about them) and it does help variables in order.

    These tutorials will accelerate the learning process, good work!
  • Trying out the system in a test setup now and it works fantastically. I love it!

    I've run into a snag though. I'm trying to set it up so clicking the link "Fire gun" automatically removes one bullet from chosen weapon AND calculates remaining health of the one being hit. The bullet-removal works beautifully, but I cannot figure out the formula to make the setter link do the damage.

    I assumed it would simply be something like
    [[Fire gun at yourself|Start][$player.health = $player.health-$gun.damage+$armor.rating]]
    but apparently not. It refuses to update the healthdisplay.

    Here are the two passages that I am building the test in:
    First, I've made an inventory passage containing all my stuff, like so:
    /% weapons %/
    <<set $sw38 = {name:"S&amp;W .38", maxAmmo:6, ammo:6, damage:10,}>>
    <<set $colt45 = {name:"Colt .45", maxAmmo:8, ammo:8, damage:22,}>>

    /% armor %/
    <<set $kevlar = {name:"Kevlar Vest", rating:7,}>>
    <<set $trenchcoat = {name:"Trenchcoat", rating:3,}>>

    /% player %/
    <<set $player = {name:"Player", maxHealth: 100, health: 100,}>>
    and then there's the actual passage shown on screen for the player. The top paragraph is all just debug stuff, where I slowly figure out the correct formulas for stuff and check if it works. The thing I can't get to work right now is the link where I shoot myself (yeah, I know :P Just for testing).
    <<nobr>>
    <<display "inventory">>

    You are holding a <<$gun.name>>.
    It has <<$gun.ammo>> rounds in it.
    It does <<$gun.damage>> points of damage, though only <<$gun.damage-$armor.rating>> if wearing a <<$armor.name>>.
    You are wearing a <<$armor.name>>.
    You have <<$player.health>> hitpoints.
    If hit by current weapon you will have <<$player.health-$gun.damage+$armor.rating>> hitpoints left.
    <br><br>

    [[Pick up an S&amp;S .38|Start][$gun = $sw38]]<br>

    [[Pick up a Colt .45|Start][$gun = $colt45]]<br>

    [[Put on a suit of kevlar|Start][$armor = $kevlar]]<br>

    <<if $gun.ammo gt 0>>
    [[Fire gun|Start][$gun.ammo -=1]]<br>
    [[Fire gun at yourself|Start][$player.health = $player.health-$gun.damage+$armor.rating]]<br>

    <<else>>

    [[Reload gun|Start][$gun.ammo = $gun.maxAmmo]]
    <<endif>>

    <<endnobr>>
    TLDR; my question is simply what the correct formula for this would be when it goes in a setter-link, and whether I can use a setter link to set more than one variable at the same time - remove a bullet AND inflict damage.
  • You can use a setter link to set more than one variable like this:
    [[Next][$test = 22;$test2 = 33]]
  • Your shoot yourself link works fine, but there are a couple of problems.

    The first is that you are reseting the player object each time. You click the link, the hgits get taken off the player object, and you go to the start page. First thing that happens there is that the inventory passage is called in, and player object reset to 100 hits.

    The easy way around that is to break start into two passages. The first passage calls in ventory, then you go to a play page, and you keep looping around that page.

    The second problem is it screws up if you do not pick armour and weapon before shooting. You need some default values.


    I did wonder if a better way to set up the objects is in a script:
    // weapons<br />$sw38 = {name:&quot;S&amp;W .38&quot;, maxAmmo:6, ammo:6, damage:10,};<br />$colt45 = {name:&quot;Colt .45&quot;, maxAmmo:8, ammo:8, damage:22,};<br /><br />// armor<br />$kevlar = {name:&quot;Kevlar Vest&quot;, rating:7,};<br />$trenchcoat = {name:&quot;Trenchcoat&quot;, rating:3,};<br /><br />// player<br />$player = {name:&quot;Player&quot;, maxHealth: 100, health: 100,};

    For some reason that does not work. I would be curious why not (I would guess it has to do with scope).
  • Bluntie wrote:
    I assumed it would simply be something like
    [[Fire gun at yourself|Start][$player.health = $player.health-$gun.damage+$armor.rating]]
    but apparently not. It refuses to update the healthdisplay.


    Without testing (or reading any other replies as I'm in a hurry), that doesn't look like it will work the way you want it to work. You don't want damage to increase as the armor value gets higher, I doubt.

    Try this:
    [[Fire gun at yourself|Start][$player.health = $player.health - ($gun.damage - $armor.rating)]]
    Though that's not how I would handle the whole thing at all.

    Also, you're going back to Start. Are you certain you're not re-initializing your objects? Your setter-link probably changes the values, then you go back to Start and set them to the default value again.
  • @Bawpie: Brilliant! That's the one I was missing, thank you very much. :D

    @Pixie (and Sharpe): So I'm resetting everything by going to the start page? That might just be it...though the "current ammo in gun" thing updates perfectly, so I assumed health would as well. But you're probably right - I shall attempt to split the start page up and only visit it once when beginning the game. Thanks!

    @Sharpe: That looks more correct indeed. My original math is baaad, shall try your formula instead.
    If you got a better way to handle something like this though, I'd be more than interested in hearing it. I'm making all of this up as I go, so smarter approaches are always welcome. Just...you know...keep it on a level I can follow. :D
  • [quote]That looks more correct indeed. My original math is baaad, shall try your formula instead.
    Actually they do exactly the same thing!

    Here is what I would do. Have a start page that calls inventory to set stuff up, and have a second page, say called "Play", where you pick up guns, shoot, etc.

    The inventory page could look like this (with everything inside one <<set>> macro):

    &lt;&lt;set //initialise everything<br /><br />//weapons<br />$sw38 = {name:&quot;S&amp;W .38&quot;, maxAmmo:6, ammo:6, damage:10,};<br />$colt45 = {name:&quot;Colt .45&quot;, maxAmmo:8, ammo:8, damage:22,};<br /><br /><br />//armour<br />$kevlar = {name:&quot;Kevlar Vest&quot;, rating:7,};<br />$trenchcoat = {name:&quot;Trenchcoat&quot;, rating:3,};<br />$none = {name:&quot;No armour&quot;, rating:0,};<br />$armor = $none<br /><br />//player<br />$player = {name:&quot;Player&quot;, maxHealth: 100, health: 100,}<br />&gt;&gt;

    However, the important changes are to have a "no armour" armour, and to set $armor to that, so you know $armor.rating always has a value.

    Then I would put this inside a script passage:

    window.shootmyself = function(p, g, a) {<br />&nbsp; d = g.damage - a.rating;<br />&nbsp; if (d &lt; 1) d = 1;<br />&nbsp; p.health -= d;<br />&nbsp; g.ammo--;<br />};

    What this does is define a function called "shootmyself", that will take three parameters; player, gun and armour. It works out the damage, to a minimum of 1 (you might prefer 0), and takes that off the player health, and also reduces the ammo by one.

    just to explain that last but one line, I have not seen anyone mention this, but a quick way to increase a value by 1 is like this:

    value++;

    And to reduce it by 1 like this:

    value--;

    The link in your game passage then looks like this:

    [[Shoot|Play][shootmyself($player, $gun, $armor)]]

  • A comment about this statement:
    <<set $gun = $colt45>>
    If this turns into a normal javascript assignment statement, then it's a set by reference - and a potential source of many surprises.  I ran across issues on another platform because javascript uses set by reference for objects rather than set by value (which it uses for simple values).

    Set by reference turns $gun into a pointer to the $colt45 structure - and any changes made to $gun would be permanently made to $colt45 - so if later on I did $gun = $pistol, then $gun = $colt45 because they'd picked up another, supposedly fully loaded colt, my new colt .45 would only have as many bullets left as the original (because after doing $gun = $golt45, $gun.bullets -= 1 is the same as $colt45.bullets -= 1).

    To get around this we need some sort of clone function - but I don't know enough about twine and javascript to work out which of the many alternatives (http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object) would be the best - or how to import such a function into twine.

    For something like $gun you could set up a specific copy macro:
    <<set $weapon = $colt45>><<wield>>
    In the wield passage:
    <<set $player.gun.name to $weapon.name>>
    <<set $player.gun.maxAmmo to $weapon.maxAmmo>>
    <<set $player.gun.ammo to $weapon.ammo>>
    <<set $player.gun.damage to $weapon.damage>>
    ...then remember only to work with the $player.gun values (although you could <<set $gun to $player.gun>> to get a shortcut to them.

    Although the wiki references a parameter function, which might be able to make something like <<wield $colt45>> work - all the examples of adding macros seem to imply that your need to use javascript - are they pre 1.4.1 or is the parameter function there to support javascript macros?
  • Bluntie wrote:
    If you got a better way to handle something like this though, I'd be more than interested in hearing it.


    Not trying to be mean, but you still have not downloaded and reviewed my (super crappy) Twine 23 challenge game as I twice advised you to do. So, I'm going to have to stop answering your questions now.

    The wrote:
    window.shootmyself = function(p, g, a) {
      d = g.damage - a.rating;
      if (d < 1) d = 1;
      p.health -= d;
      g.ammo--;
    };

    What this does is define a function called "shootmyself", that will take three parameters; player, gun and armour. It works out the damage, to a minimum of 1 (you might prefer 0), and takes that off the player health, and also reduces the ammo by one.

    The link in your game passage then looks like this:

    [[Shoot|Play][shootmyself($player, $gun, $armor)]]


    While I would advise against recommending total beginners to write JS scripts for Twine (especially in place of things that are easily handled by Twine code), that's a really good example of a simple JS function for use in Twine. Much better example than the one I wrote that sorts objects by properties. Awesome! :)

    I need to do a beginner's guide to JS for Twine one of these days.

    mykael wrote:
    In the wield passage:
    <<set $player.gun.name to $weapon.name>>
    <<set $player.gun.maxAmmo to $weapon.maxAmmo>>
    <<set $player.gun.ammo to $weapon.ammo>>
    <<set $player.gun.damage to $weapon.damage>>


    I personally would not do that. Just me, but for a number of reasons, I would keep the equipment (and just about all other) objects separate from the $player object. I feel you and I've done it that way before, but I'm no longer of that same opinion.

    Also, to address the other issue you mentioned, sometimes I'll just do:
    <<set $foo = $oldFoo>>
    <<set $foo = $newFoo>>
    Then, if I want to go back to the previous $foo, I just do:
    <<set $foo = $oldFoo>>
    I should add that to the example program. Of course, I don't do that for inventories where you'll want to swap between several items, but that works for a number of other things.

    For inventories, I use lists/arrays, and that's getting into another area best left for another thread. I'd like to keep this as close to using objects as we can manage. ;)
  • mykael wrote:
    If this turns into a normal javascript assignment statement, then it's a set by reference - and a potential source of many surprises.  I ran across issues on another platform because javascript uses set by reference for objects rather than set by value (which it uses for simple values).

    Set by reference turns $gun into a pointer to the $colt45 structure - and any changes made to $gun would be permanently made to $colt45 - so if later on I did $gun = $pistol, then $gun = $colt45 because they'd picked up another, supposedly fully loaded colt, my new colt .45 would only have as many bullets left as the original (because after doing $gun = $golt45, $gun.bullets -= 1 is the same as $colt45.bullets -= 1).

    Is that not what you want to happen though? If the player drops the colt, and then picks it up again, it should have the same number of bullets in it. Now, if you are writing a game where rooms are generated at random (rogue-like style), then yes, you might want to clone objects to go in each room, but otherwise, I would not.
  • As noted by The Pixie, if the item you're equipping is an item you've used before, then you likely do not want to clone.  On the other hand, if the player finds a new item of the same type, then yes, you probably would want to make a clone.

    There's also no need to go looking for a clone function, as the headers already include one, clone() (e.g. <<set $gun = clone($colt45)>>).

    Honestly though, if you're going to get into creating new instances of items a lot, I'd make a simple constructor for the item, rather than cloning new instances of base objects all the time.
  • @Sharpe: Please blame it on tunnel vision and my memory being like a sieve rather than me trying to be deliberately rude. Your answers have been nothing but incredibly helpful. So far I'm just taking it all one step at a time and treating every obstacle like an isolated "aha!"-experience...and since I've mostly gotten the answers I needed from the forum, not to mention enjoy discussing directly with y'all, I haven't gotten around to checking out your no doubt excellent link yet. But I've downloaded it now and will try to look it over in Twine later today. Can't promise I'll get around to actually *playing* it for a few days, but shall strive to do so and leave you a proper review afterwards.

    @Pixie: Hooray! You're describing pretty much exactly what I did after yesterday's last round of answers, and that works brilliantly. And adding a $noArmor and a $noGun solved alot of problems indeed. :D The script looks lovely, shall have to try it. Till now I've just been terrified of using actual scripts on account of noobness...I try to make a point of only putting stuff in my projects that I can actually look at and understand what is going on. Slowly learning though, and this looks pretty simple. Thanks!

    @Mykael:
    Hmmmmm...so far my guns seem to be fully loaded every time I switch to a new one, so I don't think the problem you're talking about has happened to me yet. Though, as Pixie points out, I'd actually like the guns to "remember" the number of bullets in it regardless of weapon-swapping - I've just ignored that particular problem till I got the guns to work in the first place. :D
    I shall definitely refer to this when and if it occurs though - never hurts to be prepared. Thanks! :)
  • Bluntie wrote:
    @Sharpe: Please blame it on tunnel vision and my memory being like a sieve rather than me trying to be deliberately rude. Your answers have been nothing but incredibly helpful. So far I'm just taking it all one step at a time and treating every obstacle like an isolated "aha!"-experience...and since I've mostly gotten the answers I needed from the forum, not to mention enjoy discussing directly with y'all, I haven't gotten around to checking out your no doubt excellent link yet. But I've downloaded it now and will try to look it over in Twine later today. Can't promise I'll get around to actually *playing* it for a few days, but shall strive to do so and leave you a proper review afterwards.


    Okay, that's fine. No need to play it or review it (it's terrible). It's just an RPG combat system.
  • Thanks for all these details, it's a revelation that Javascript is so accessible in Twine. That means once you're confident with the Twine basics, you can do pretty much anything you want!

    [I'm not strictly a newbie...

    I remember back to when http://gimcrackd.com/etc/src/ didn't redirect to twinery.org and Twee and Twine were experimental tools for story generation. I don't think that macros and full Javascript features were really pushed back then - it all seemed like it was only designed for interactive branching stories and not a lot else.

    At some point I went off chasing the new-fangled Inform7 because it was so full of features it just had to be great, good and the best.

    Now I'm back here instead  :-X

    As a seasoned programmer, I like what I see under the skin of Twine - I still use 'classic' Tiddlywiki all the time because it's still such a great concept.]

    Really looking forward to regenerating an existing wiki-based game in a totally new way with Twine!

    Once again, thanks for all the useful info! Didn't intend to hijack the thread!  ;)
  • This made things a lot easier for my game.

    One of the most useful thing with this is having them as args[] in SugarCube widgets.

    Here's an example :

    Start:
    <<set $player = {
    str: 5,
    dex: 5,
    health: 3,
    }>>
    <<set $enemy = {
    str: 5,
    dex: 5,
    health: 3,
    }>>



    So you get a character and an enemy with 3 stats.  Let`s say that the rule to loose health in a combat is to have a dexterity inferior to opponents str.

    Combat :
    <<widget "loosingHealth">>
    <<if $args[0].dex < args$[1].str>>
    <<set $args[0].health -=1>>
    <<endif>>
    <<if $args[1].dex < args$[0].str>>
    <<set $args[1].health -=1>>
    <</widget>>


    Now you can call your widget without sending every variables, you can send the object and everything will follow.

    <<loosingHealth $player $enemy>>

    Now, you can create an unlimited number of characters and send them in that widget.




  • TheMadExile wrote:

    As noted by The Pixie, if the item you're equipping is an item you've used before, then you likely do not want to clone.  On the other hand, if the player finds a new item of the same type, then yes, you probably would want to make a clone.

    There's also no need to go looking for a clone function, as the headers already include one, clone() (e.g. <<set $gun = clone($colt45)>>).

    Honestly though, if you're going to get into creating new instances of items a lot, I'd make a simple constructor for the item, rather than cloning new instances of base objects all the time.


    Can you explain Constructors? 
  • Constructor is what is know as a design pattern, it helps with creating instances of java-script objects, another useful one is the Factory patten.

    A book I liked about java-script related design patterns is Learning JavaScript Design Patterns
  • I found that book to be somewhat muddled.  It's also targeted at professional developers, so I wouldn't exactly call it beginner friendly.

    As to explaining constructors.  So far you've probably seen things like this: (using the colt45 example from before)

    /% Setup the base "colt45" object %/
    <<set
    $colt45 =
    {
    name : "Colt .45"
    , maxAmmo : 8
    , ammo : 8
    , damage : 22
    };
    >>

    /% Whenever you need a new instance of a "Colt .45", you have to clone the base object %/
    <<set $gun = clone($colt45)>>
    In most cases, there's absolutely no reason to have instances of these objects lying around.  It's often better to simply have some sort of construction function that returns new instances of the object (an actual constructor or a factory, whatever).

    A very basic constructor example:

    /% Setup the "Colt45" constructor (this would be best in a script passage) %/
    window.Colt45 = function ()
    {
    this.name = "Colt .45";
    this.maxAmmo = 8;
    this.ammo = 8;
    this.damage = 22;
    };

    /% Whenever you need a new instance of a "Colt .45", you call its constructor with the new operator %/
    <<set $gun = new Colt45()>>
    You could easily make that more generic as well.  For instance, you could have a Gun constructor which would take some number of arguments that determined what type of gun object it would yield.
  • TheMadExile wrote:

    I found that book to be somewhat muddled.  It's also targeted at professional developers, so I wouldn't exactly call it beginner friendly.

    I suggested that book if they are interested in learning about patterns only because a) it targets java-script and b) its on-line.
    If you know of a better one for beginners then please link it so I can add it to my list.

    The ones I normally use (GoF and a Java related one) either a) are C / Java based, or b) cost money.
  • Nothing fancy to add, just wanted to say THANK YOU SO MUCH SHARPE!
  • You're very welcome.

    Have you found an answer or figured out your double-pane question in the other thread?
  • Hey, thanks so much for this information. It's helped me a lot. however, I'm having a hard time accessing javascript object information that I set in Twine passages using javascript mods.

    For example, I'm making Snowpiercer parody game where you have to move up a train a fight people in train cars. Each train car has a "power" that represents your chance of beating it. That's $thiscar.power. However, when I try to access it in a script using
    state.history[0].variables["thiscar.power"]
    I get "undefined" back. Is there a different way I'm supposed to be doing this?
  • Assuming a vanilla story format:

    // square bracket notation
    state.history[0].variables["thiscar"].power

    // dot notation
    state.history[0].variables.thiscar.power
    In SugarCube:

    // square bracket notation
    state.active.variables["thiscar"].power

    // dot notation
    state.active.variables.thiscar.power
  • Thanks for posting about this Sharpe!  :)
    I'll have to rummage through this forum to pick up all the tips I can once I'm integrating everything together.
Sign In or Register to comment.