Permanently Rotating an Object

Started by PMick, Sat 13/07/2024 21:10:50

Previous topic - Next topic

PMick

I've spent the last few hours trying to figure out how to do this--been on multiple help sites, including these forums, trying to figure this out and at last I'm throwing in the towel and asking for help.

Here's what I want to do: I have several objects on the screen. I want the player to be able to click on any of these object and have it rotate 90 degrees, keeping that position until the player clicks again to rotate it another 90 degrees. The most obvious way to do this, as far as I can tell, is with an array of DynamicSprites and the DynamicSprite.Rotate command. The problem I'm running into is that I need the DynamicSprite array to be global, so that the effect of clicking an object can last until the player changes it again.  I've tried several different ways to declare the DynamicSprite array outside the function, including in game_start, but when I try, inside the function, to change the values in the array, I'm told I can't assign initial values to a global variable.

I don't necessarily have to use DynamicSprites for this, but I'd like to as it seems like that would be the most efficient coding. The other way I know of to do this would be to have 4 different sprites (created from images I manually rotate myself) for each object, but there are 25 objects on the screen, and my plan is to have several different screens set up this same way. So while this method would avoid the frustrations of DynamicSprites, it would involve making literally hundreds of sprites and taking a very long time.

I'm really hoping there is a way to do this with DynamicSprites, to save me the hours of manipulating graphics. Can someone help me? Again, the goal is to have 25 objects on the screen, and every time the player clicks one, it rotates 90 degrees and stays that way until clicked again.

Crimson Wizard

#1
If you need this only in one room, then declare the array in a room script, outside of any functions:

Code: ags
DynamicSprite* ObjectSprites[];

The above is just a pointer to array, initialized with null, (as with any dynamic objects in ags). So you must first create the array in memory.
Creation of an object is a operation, and in AGS script you cannot do that outside of a function. Therefore you need to use some function.
A good option is to do this on room's "Before fade-in" event:

Code: ags
function room_Load()
{
    ObjectSprites = new DynamicSprite[Room.ObjectCount];
}

This creates an array of N slots, each of which is initialized with a null (again), but may be filled with a DynamicSprite object.
Note, that you may want to also delete this array with all the sprites when leaving the room, to save the memory. This may be done, let's say, in "Leave after fade-out":

Code: ags
function room_Unload()
{
    // this is enough to delete array and all sprites contained within,
    // as AGS automatically deletes a dynamic object, when there's no pointer variable referencing it;
    // so assigning an array with "null" will leave no reference to array, and array will get cleaned up.
    // so long as you do not assign DynamicSprite's pointers to other non-local variables,
    // all the sprites within will get deleted as well!
    ObjectSprites = null;
}

From now on you may add DynamicSprites into this array slots, whenever you need to

Code: ags
function SomeObject_AnyClick(Object *theObject, CursorMode mode)
{
    DynamicSprite* spr = DynamicSprite.CreateFromExistingSprite(theObject.Graphic);
    spr.Rotate(90);
    ObjectSprites[theObject.ID] = spr;
    theObject.Graphic = spr.Graphic;
}

Above are trivial examples, and the code should be adjusted for your needs.

PS. I might add that is said above is not exclusive to DynamicSprites, but to any dynamic array of anything.

PMick

#2
Ok, I think I see what you've done there. We're creating a room array (maybe that's the same as a global array--not sure). Then within the function we're creating a temporary Dynamic Sprite to work with, and then copying that temporary DynamicSprite into the more permanent room DynamicSprite. One question, though--why is the last line of SomeObject_AnyClick "theObject.Graphic = spr.Graphic?" Shouldn't it be "theObject.Graphic = ObjectSprites[theObject.ID"? If I assign the object's graphic to spr, which was declared within the function, won't spr (and therefore the sprite the object is using) disappear at the end of the function?

Apologies for my newbie questions. Still trying to understand DynamicSprites.

EDIT: I tried doing exactly as you suggested, but I still don't understand how that worked. If you wouldn't mind answering the question above, I'd really like to understand better how this works.

Crimson Wizard

#3
Quote from: PMick on Sat 13/07/2024 22:30:24Then within the function we're creating a temporary Dynamic Sprite to work with, and then copying that temporary DynamicSprite into the more permanent room DynamicSprite.

To clarify, this does not copy "temporary" DynamicSprite into a "permanent" DynamicSprite, this saves a pointer to DynamicSprite into the array. Array contains not sprites themselves, but pointers to them.
By saving a pointer to DynamicSprite into array, you are sort of making it permanent, as AGS will keep it in memory for so long as something "references" it.

array looks something like this:
Code: ags
    array                     somewhere else in memory
       [ 0 ]  pointer      ->  dynamic sprite 0
       [ 1 ]  pointer      ->  dynamic sprite 1
       [ 2 ]  nothing
       [ 3 ]  nothing
       [ 4 ]  nothing


Quote from: PMick on Sat 13/07/2024 22:30:24One question, though--why is the last line of SomeObject_AnyClick "theObject.Graphic = spr.Graphic?" Shouldn't it be "theObject.Graphic = ObjectSprites[theObject.ID"? If I assign the object's graphic to spr, which was declared within the function, won't spr (and therefore the sprite the object is using) disappear at the end of the function?

That's because Object.Graphic property is integer, and is meant to store the sprite's number, not a pointer. DynamicSprite.Graphic tells the number (ID), under which this dynamic sprite is registered in the game. You may assign this Graphic number to anything, even to multiple things at once, similar to how you assign regular sprite numbers.

The DynamicSprite won't disappear after existing this function, because its pointer is already stored in the array. That's why you need an array in the first place.


PMick

#4
Ah, ok. I think I see now. Having trouble making this work in a single function for any Object, but if I can't figure out what I'm doing wrong I'll post again. I can at least get it to work for one object right now.

Crimson Wizard

Quote from: PMick on Sun 14/07/2024 02:01:54Having trouble making this work in a single function for any Object, but if I can't figure out what I'm doing wrong I'll post again. I can at least get it to work for one object right now.

What is the trouble? I believe that the above code might work if you put "SomeObject_AnyClick" function in each relevant Object's "AnyClick" event.

PMick

I'm sure it would have. I was trying to see if I could be more efficient by giving the code a separate function and then calling that function from each of the AnyClicks. I figured what I was doing wrong there on my own and it works great! Thanks again!

Crimson Wizard

#7
Quote from: PMick on Sun 14/07/2024 20:26:57I was trying to see if I could be more efficient by giving the code a separate function and then calling that function from each of the AnyClicks.

That should also work, it's just another way of doing this. For example:

Code: ags
function RotatingObject_AnyClick(Object *theObject)
{
    DynamicSprite* spr = DynamicSprite.CreateFromExistingSprite(theObject.Graphic);
    spr.Rotate(90);
    ObjectSprites[theObject.ID] = spr;
    theObject.Graphic = spr.Graphic;
}


function oObject1_AnyClick(Object *theObject, CursorMode mode)
{
    RotatingObject_AnyClick(theObject);
}

function oObject2_AnyClick(Object *theObject, CursorMode mode)
{
    RotatingObject_AnyClick(theObject);
}

function oObject3_AnyClick(Object *theObject, CursorMode mode)
{
    RotatingObject_AnyClick(theObject);
}

// etc


Or, if this is an older version of AGS, where the interaction functions did not have objects passed as parameters:
Code: ags
function oObject1_AnyClick()
{
    RotatingObject_AnyClick(oObject1);
}

function oObject2_AnyClick()
{
    RotatingObject_AnyClick(oObject2);
}

function oObject3_AnyClick()
{
    RotatingObject_AnyClick(oObject3);
}

PMick

Yeah, that's exactly what I did. Different function name, but same coding structure.

SMF spam blocked by CleanTalk