Author Topic: Text boxes  (Read 3120 times)

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Text boxes
« on: Fri, Nov 26, 2010 »
I am planning on making, for my platformer, signposts throughout early levels, so that the player can go up to them, press K, and a text box will pop up at the top of the screen saying things such as "press up to go in the next door" or "you need a key to open this gate". I want to create objects called MessageSpots, which I pass in their message string during their construction along with their X and Y values.

I just need help on how to make the message display as a flxText from within the object, but I don't know where to start. Any help would be great.

Placeable

  • Member
  • **
  • Posts: 49
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #1 on: Fri, Nov 26, 2010 »
Let the object/class have a FlxText object or simply add it in the constructor.  Your MessageSpot class should extend FlxGroup, in my opinion.  

Then you could use add(YourFlxText) to it.

Code: [Select]
public class MessageSpot extends FlxGroup
{

       public function MessageSpot(_x:int, _y:int, _text:String):void
       {
             super();
             scrollFactor.x = 0;
    scrollFactor.y = 0;
             add(new FlxText(0, 0, 100, _text), true);
             reset(_x, _y)   //  Use the reset call AFTER add(...)
       }
}


Then you would just create and add you MessageSpot object at whatever.
« Last Edit: Fri, Nov 26, 2010 by Placeable »

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #2 on: Fri, Nov 26, 2010 »
Almost got it.

What I've done in MessageSpot.as is:
Code: [Select]
public class MessageSpot extends FlxGroup
{
[Embed(source = "data/messagepoint.png")] private var ImgMessageSpot:Class;

protected var _messageText:FlxText;

public function MessageSpot(X:Number, Y:Number, message:String)
{
super();
scrollFactor.x = scrollFactor.y = 0;
reset(X, Y);
_messageText = new FlxText(10, 50, FlxG.width - 20, message);
_messageText.scrollFactor.x = _messageText.scrollFactor.y = 0;
add(_messageText);
add(new FlxSprite(X, Y, ImgMessageSpot));
}
}

and in PlayState's construction:
Code: [Select]
add(_enemies);
add(_playerGroup);
add(_keys);
add(_throwKnives);
add(_doors);
add(_acid);
add(_water);
add(tiles);
add(_saveSpots);
add(new MessageSpot(160, 2050, "this is a test string"));

This puts my question mark bubble at the right spot in the level, and the text is displayed near the top of the screen (I'll make it appear/disappear later).

But when I changed PlayState to allow for grouping the messageSpots in a group into this:
Code: [Select]
_messageSpots.add(new MessageSpot(160, 2050, "this is a test string"));

add(_enemies);
add(_playerGroup);
add(_keys);
add(_throwKnives);
add(_doors);
add(_acid);
add(_water);
add(tiles);
add(_saveSpots);
add(_messageSpots);

I'm getting an error, saying:

[Fault] exception, information=TypeError: Error #1009: Cannot access a property or method of a null object reference.

and it refers to the line saying _messageSpots.add(new Messag.....

Do you see what I'm doing wrong? I've gotten grouping to work with all my other level objects, but this is seeming to fail.

SCPM

  • Member
  • **
  • Posts: 78
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #3 on: Fri, Nov 26, 2010 »
Did you create the new FlxGroup?

In the PlayState class you ought to have:
Code: [Select]
private var _messageSpots:FlxGroup;
and in the function you ought to have:
Code: [Select]
_messageSpots = new FlxGroup;
Otherwise, I don't see why it shouldn't work.

Placeable

  • Member
  • **
  • Posts: 49
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #4 on: Fri, Nov 26, 2010 »
Oops, my bad.  You have to call the   reset(_x, _y)  function after the add(...) call if you want to move the children to the group.  But in the constructor it will also work to set x & y in the normal way:

Code: [Select]
x = _x;
y = _y;

But if you're moving the group after that you should use reset(...).  I'll update my post above to reflect this.

Oh and the reason why you get the error superbany is probably what SCPM said.  Did you forget to instantiate your _messageSpots object?


superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #5 on: Fri, Nov 26, 2010 »
I feel so dumb. I forgot to do "_messageSpots = new FlxGroup();"
Its all working now.

For future reference of anybody who visits this thread, my full code is:
In PlayState:
Code: [Select]
protected var _messageSpots:FlxGroup;

override public function create():void
{
_messageSpots = new FlxGroup();

_messageSpots.add(new MessageSpot(160, 2050, "this is a test string"));

add(_messageSpots);
}

override public function update():void
{
        //decide whether or not to display the text with [object].setDisplay();
}
and in MessageSpot:
Code: [Select]
public class MessageSpot extends FlxGroup
{
[Embed(source = "data/messagepoint.png")] private var ImgMessageSpot:Class;

protected var _messageText:FlxText;

public function MessageSpot(X:Number, Y:Number, message:String)
{
super();
scrollFactor.x = scrollFactor.y = 0;
reset(X, Y);
_messageText = new FlxText(10, 50, FlxG.width - 20, message);
_messageText.scrollFactor.x = _messageText.scrollFactor.y = 0;
add(_messageText);
add(new FlxSprite(X, Y, ImgMessageSpot));
}

public function setDisplay(display:Boolean):void
{
_messageText.visible = display;
}
}

and it seems to be working.

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #6 on: Sat, Nov 27, 2010 »
Aw, I feel like giving up. I've gotten other overlap callback functions to work, but this one is mysteriously failing. See any problem?

PlayState:
Code: [Select]
//update():
super.update();

FlxU.overlap(_player, _water, waterOverlapped);
FlxU.overlap(_player, _acid, acidOverlapped);
FlxU.overlap(_player, _messageSpots, messageOverlapped);
FlxU.collide(_player, _objects);

//messageOverlapped():
protected function messageOverlapped(Object1:FlxObject, Object2:FlxObject):void
{
//Checks that the message is already not being shown and that K has just been pressed
if (!((Object2 as MessageSpot).showDisplay()) && (FlxG.keys.justPressed("K")))
{
(Object2 as MessageSpot).setDisplay(true);
}
}

MessageSpot:
Code: [Select]
protected var _messageText:FlxText;

public function MessageSpot(X:Number, Y:Number, message:String)
{
super();
scrollFactor.x = scrollFactor.y = 0;
reset(X, Y);
_messageText = new FlxText(10, 50, FlxG.width - 20, message);
_messageText.scrollFactor.x = _messageText.scrollFactor.y = 0;
add(_messageText);
add(new FlxSprite(X, Y, ImgMessageSpot));
}

override public function update():void
{
//Hides the text every frame
_messageText.visible = false;
super.update();
}

public function showDisplay():Boolean
{
//Returns if the text is visible
return (_messageText.visible);
}

public function setDisplay(display:Boolean):void
{
//Sets the text to visible or not
_messageText.visible = display;
}

Everything seems to work perfectly except for the first line of messageOverlapped, which gets the error saying "[Fault] exception, information=TypeError: Error #1009: Cannot access a property or method of a null object reference.". I'm not seeing anything wrong with this, can anybody help?

Placeable

  • Member
  • **
  • Posts: 49
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #7 on: Sat, Nov 27, 2010 »
Hmm, not sure.  It looks correct.  Usually when I get errors like this (seems like you are trying to access a null object?) I simply trace the objects to see what I am actually getting.

Code: [Select]
FlashConnect.trace("Obj1: " + Object1 + " Obj2: " + Object2);

You could also try and set the callback function to this:
Code: [Select]
messageOverlapped(Object1:FlxObject, Object2:MessageSpot):void

To skip the  as  operator.  My advice to you is to debug it all (remove the if statement completely first), one step at a time to find the culprit.

Good Luck

SCPM

  • Member
  • **
  • Posts: 78
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #8 on: Sat, Nov 27, 2010 »
In the FlxGroup documentation it says this:
Quote
NOTE: Although FlxGroup extends FlxObject, it will not automatically add itself to the global collisions quad tree, it will only add its members.
So it may be that when the FlxGroup _messageSpots is passed to overlap, it doesn't pass MessageSpot (also a FlxGroup), but the members of MessageSpot.  Thus Object2 will have a value of a FlxSprite, and setting Object2:MessageSpot will produce a coercion failure.  I must warn you that I'm just assuming, since I'm new to all of this.

Also, I think that you should delete this:
Code: [Select]
override public function update():void
{
//Hides the text every frame
_messageText.visible = false;
super.update();
}
or else your text will never appear.  Instead, use setDisplay in the update function of the PlayState.

But if you want to use the overlap, you should either rethink the code, or that particular portion of the game's interface.

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #9 on: Sat, Nov 27, 2010 »
I did what you said, and with tracing I got:
Object1: Player   Object2: FlxSprite

So it seems that messageOverlapped is getting the sprite, since that is the part that is overlapping the player, and not the entire _messageSpot group.

Having "messageOverlapped(Object1:FlxObject, Object2:MessageSpot)" gets the error: [Fault] exception, information=TypeError: Error #1034: Type Coercion failed: cannot convert org.flixel::FlxSprite@63ee241 to MessageSpot.

Is there any way to have an object, and refer to the group it is a part of? Such as a method or template that you would put in the <messagespot> FlxSprite and get the group <_messageSpot>? Because I don't see any other way to get signposts to work otherwise. Thanks.

Placeable

  • Member
  • **
  • Posts: 49
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #10 on: Sat, Nov 27, 2010 »
Quote
Is there any way to have an object, and refer to the group it is a part of?

Not that I know of, however you could add it as a parameter in the constructor for the MessageSpot object, as a reference.  Like a 'parent'.

However, like SCPM said, I think that's where your problem is.  You are currently trying to overlap a group holding groups.  This might cause a problem, I have never tried this myself so I am not sure.

If your game is linear with MessageSpots, ie one at a time, they all come in a fixed order, you should probably just use one MessageSpot object and reuse it after the previous one was shown.  That way you can do a working overlap check on just one group (The MessageSpot).

But personally I would make a Trigger class, a simple object extending FlxSprite.  I could add these triggers to a group and check if the player is overlapping with them.  Then, if true, the callback function for the overlap would call the specific trigger's (the one the player overlaps) callback function which in turn would create your MessageSpot object.

Simple code:

Trigger objects could be defined beforehand with the properties of how they
should create a MessageSpot object.
Code: [Select]
In PlayState.as
FlxU.overlap(triggers, player, HandleTrigger); //::  triggers is a FlxGroup

private function HandleTrigger(obj:Trigger, plr:Player):void
{
     obj.callBack();
}

In Trigger.as
private function callBack():void
{
    /* Create MessageSpot or whatever
       The trigger could have a reference to a CreateMessageSpot(...)
       function in PlayState and a fixed number of parameters.
    */
}

This can be a bit rough but doing it right you can make Triggers with an x amount of arguments for their callback functions (which also could be defined in the constructor of the trigger). So your trigger objects would not be limited to call only the creation of a MessageSpot object.
« Last Edit: Sat, Nov 27, 2010 by Placeable »

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #11 on: Sat, Nov 27, 2010 »
Thanks for the advice. My plan to get this working so far is this:

1) Create a FlxText "_message" in PlayState, which will be the constant text box for all signpost/character messages

2) MessageSpot.as will extend FlxSprite instead of FlxGroup. Each one will be created with a message string passed into them at construction, which will then be given to the variable "_storedMessage" within each MessageSpot object.

3) I will overlap between the "_player" object and "_messageSpots" group in PlayState, which will call getMessage(), which will return the embedded string, which I can then set the "_message" field's text to whatever string is gotten.

Sample planned code (roughly):
PlayState:
Code: [Select]
override public function create():void
{
        _message = new FlxText
        _messageSpots = new FlxGroup()
_messageSpots.add(new MessageSpot(160, 2050, "a string"))
}

override public function update():void
{
        super.update
        overlap(_player,_messagespots,messageOverlapped)
}

public function messageOverlapped(obj1:FlxObject,obj2:FlxObject):void
{
        _message.text = (obj2 as MessageSpot).getMessage()
}

and in MessageSpot:
Code: [Select]
public function MessageSpot(X:Number, Y:Number, MESSAGE:String)
{
super(X,Y);
_storedMessage = MESSAGE;
loadGraphic(ImgMessageSpot);
}

public function getMessage():string
{
        return _storedMessage;
}

That looks like it should work. Any opinions before I try to change it?

Placeable

  • Member
  • **
  • Posts: 49
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #12 on: Sat, Nov 27, 2010 »
Looks fine to me, this should do the trick.  Have nothing to add other than clean up the code and optimize if you can :)  Good luck!

cai

  • Contributor
  • ****
  • Posts: 465
  • Karma: +0/-0
  • the illest of villains
    • View Profile
    • Brandon Cash
Re: Text boxes
« Reply #13 on: Sat, Nov 27, 2010 »
That all looks good, but it will also execute every frame.  You probably want to make sure it only executes once when entering and exiting the area.  You can use the FlxText's .visible to keep track of that.
Follow me on Twitter | Come join us at #flixel on irc.freenode.net!

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #14 on: Sun, Nov 28, 2010 »
I'll be trying this. Once I've gotten the basic framework down, I'll work on those details.

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #15 on: Sun, Nov 28, 2010 »
Success! I'll still have to work on some details, but here's the final code (for now):
PlayState:
Code: [Select]
protected var _messageText:FlxText;
protected var _messageSpots:FlxGroup;

override public function create():void
{
_messageSpots = new FlxGroup();
_messageSpots.add(new MessageSpot(160, 2050, "This is a test string."));
_messageSpots.add(new MessageSpot(190, 2050, "This one is also a test string, only it is longer than the other one, and therefore goes onto another line."));
add(_messageSpots);

_messageText = new FlxText(10, 50, FlxG.width - 20);
_messageText.scrollFactor.x = _messageText.scrollFactor.y = 0;
add(_messageText);
}

override public function update():void
{
_messageText.text = "";
super.update();
FlxU.overlap(_player, _messageSpots, messageOverlapped);
}

protected function messageOverlapped(Object1:FlxObject, Object2:MessageSpot):void
{
_messageText.text = Object2.getMessage();
}

In MessageSpot:
Code: [Select]
public class MessageSpot extends FlxSprite
{
[Embed(source = "data/messagepoint.png")] private var ImgMessageSpot:Class;

protected var _storedMessage:String;

public function MessageSpot(X:Number, Y:Number, message:String)
{
super(X, Y, ImgMessageSpot);

_storedMessage = message;
}

public function getMessage():String
{
return _storedMessage;
}
}

I might keep it as it is currently that messages automatically pop up/disappear only when the player is overlapping the message bubble.

To see it in action: http://www.swfcabin.com/open/1290963516.
Press ~` to see the log, which shows what the current text for _messageText is.
Press B to see the bounding boxes.

Placeable

  • Member
  • **
  • Posts: 49
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #16 on: Sun, Nov 28, 2010 »
Man I am pretty sure www.swfcabin.com is banned for certain countries or something since I have never been able to access that site :S


superbany  glad you got it to work, but as cai said you should make sure to limit the messages once they display.  Consider the following code:


Code: [Select]
protected function messageOverlapped(Object1:FlxObject, Object2:MessageSpot):void
{
if (!_messageText.visible)  //::  Only do this once
        {
            _messageText.text = Object2.getMessage();
           _ messageText.visible = true;  //::  Show the text, make sure to hide it again when neccissary
        }
}

superbany

  • Member
  • **
  • Posts: 53
  • Karma: +0/-0
  • [Please enter text here]
    • View Profile
    • Superbany's games
Re: Text boxes
« Reply #17 on: Mon, Nov 29, 2010 »
I did some cleaning up, and also included some fancy fading in/out.
Since many people have trouble with swfcabin, I'll try newgrounds: http://www.newgrounds.com/dump/item/051720a5136a54a27e994162aba51217
Press B to see bounds, ~ to open the log, which says whenever the text is changed.

The new code will only change _messageText.text once each time you move onto a MessageSpot, not every frame, and will fade the text in/out.

PlayState has a new Boolean variable called _displayingMessage (as compared to _messageText.visible) which decides what changes should be made to _messageText.alpha. Here's the new code for PlayState:
Code: [Select]
protected var _messageText:FlxText;
protected var _displayingMessage:Boolean;
protected var _messageSpots:FlxGroup;

override public function create():void
{
_displayingMessage = false;

_messageSpots = new FlxGroup();
_messageSpots.add(new MessageSpot(160, 2050, "This is a test string."));
_messageSpots.add(new MessageSpot(190, 2050, "This one is also a test string, only it is longer than the other one, and therefore goes onto another line."));
add(_messageSpots);

_messageText = new FlxText(10, 50, FlxG.width - 20);
_messageText.scrollFactor.x = _messageText.scrollFactor.y = 0;
add(_messageText);
}

override public function update():void
{
super.update();

_displayingMessage = false;
FlxU.overlap(_player, _messageSpots, messageOverlapped);

if (_displayingMessage == false)
{
if(_messageText.alpha > 0)
_messageText.alpha -= .1;
else
_messageText.visible = false;
}
else if (_messageText.alpha < 1)
_messageText.alpha += .1;
}

protected function messageOverlapped(Object1:FlxObject, Object2:MessageSpot):void
{
_displayingMessage = true;
if (!_messageText.visible)
{
_messageText.text = Object2.getMessage();
_messageText.visible = true;
FlxG.log("Now changing _messageText.text");
}
}

Looks pretty much good, as far as I can tell. One bug is that going between the messageSpots before the message completely fades doesn't change it, but they will be far enough placed throughout the level that that shouldn't be a problem.
« Last Edit: Mon, Nov 29, 2010 by superbany »

Placeable

  • Member
  • **
  • Posts: 49
  • Karma: +0/-0
    • View Profile
Re: Text boxes
« Reply #18 on: Mon, Nov 29, 2010 »
Looks really good and the alpha effect was a nice touch.  Game runs smooth it will be interesting to see how the final product turns out to be.

Good luck!