How to change the texture of AnimatedSprite programatically - godot

I have created a base scene that I intend to use for all human characters of my game. I am using an AnimatedSprite where I defined different animations for the different positions of the character, all using a texture that contains all the frames.
This works for a specific character, but now I would like to create other characters. Since I am using a character generator, all the sprite sheets are basically the same, but with different clothes, accessories, etc. I would like to avoid replicating the animation definitions for the other characters. I could achieve that by setting a different texture on each instance of the scene, but I can't find a way to do it.
If I edit the tscn file and set a different image, it does what I want.
I tried updating the atlas property of the animation frames, but doing that affects all instances of the scene:
func update_texture(value: Texture):
for animation in $AnimatedSprite.frames.animations:
for frame in animation.frames:
frame.atlas = value
I also tried cloning a SpriteFrames instance, by calling duplicate(0), updating it with the above code, then setting $AnimatedSprite.frames, but this also updates all instances of the scene.
What is the proper way to change the texture of a specific instance of AnimatedSprite?

I found a solution. The problem was that the duplicate method does not perform a deep clone, so I was having references to the same frame instances.
Here's my updated version:
func update_texture(texture: Texture):
var reference_frames: SpriteFrames = $AnimatedSprite.frames
var updated_frames = SpriteFrames.new()
for animation in reference_frames.get_animation_names():
if animation != "default":
updated_frames.add_animation(animation)
updated_frames.set_animation_speed(animation, reference_frames.get_animation_speed(animation))
updated_frames.set_animation_loop(animation, reference_frames.get_animation_loop(animation))
for i in reference_frames.get_frame_count(animation):
var updated_texture: AtlasTexture = reference_frames.get_frame(animation, i).duplicate()
updated_texture.atlas = texture
updated_frames.add_frame(animation, updated_texture)
updated_frames.remove_animation("default")
$AnimatedSprite.frames = updated_frames

Related

Godot: Call external method

Lots of googling and I'm still not grasping what is probably a simple solution.
Scene: "Main". Contains a TileMap "Grid" with a script attached to it "Grid.gd".
Scene: "Player". Contains a KinematicBody2D "Player" with a script attached to it "Player.gd"
In Player.gd, I need to call a method in Grid.gd "_Calculate", pass it two variables, and have it return one variable.
var vNewPosition = Grid._Calculate(vPlayer, vInputDirection)
Error: The identifier "Grid" isn't declared in the current scope.
Obiously I need to reference the Grid.gd script somewhere to access it, but none of the many examples I have tried work.
Thanks in advance,
Josh
Given that
The Main scene has an instance of the Player scene
And
In Player.gd, I need to call a method in Grid.gd "_Calculate", pass it two variables, and have it return one variable.
I'll give you a few options.
The bad way
If Player knows the node path to Grid, it could use get_node to get it.
onready var _grid = get_node("../../grid")
Or something like that.
And then use it:
var vNewPosition = _grid._Calculate(vPlayer, vInputDirection)
This approach is, of course, not recommended. It will break if the node path is wrong.
The common way
Assuming Player does not know the node path (which is more likely), you can export a NodePath variable in Player.gd and use it to get the Grid:
exprort var grid_path:NodePath
onready var _grid = get_node(grid_path)
Then you need to set the Grid Path property in the Main scene to Grid.
This approach is better than the prior one, in that it does not depend on the path to Grid. However it still depends on a Grid being there. If Player makes no sense when there is no Grid, this approach is OK.
The good way
If Grid may or may not be there, and we want Player to work regardless, we can tweak the above approach:
exprort var grid_path:NodePath
onready var _grid = get_node_or_null(grid_path)
And remember to check if grid is null before using it:
if grid == null:
return # or whatever
var vNewPosition = grid._Calculate(vPlayer, vInputDirection)
Or like this:
var vNewPosition = grid._Calculate(vPlayer, vInputDirection) if grid != null else null
Which also allows us to specify a default value that vNewPosition will take when there is no grid (the null at the end of the line). And thus you can have the Player work when there is no Grid, and when there is it will use, and regardless of where (because Main tells it where it is).
This solution is not completely decoupled, because Player still knows grid is a thing that has _Calculate. Also we presume there is zero or one, not multiple. I can think of a way to approach that, but decoupling for the sake of decoupling is unnecessary. I'm calling this good enough for your use case. Yet, let me know if you want the over-engineered way.

Telling PixiJS that the WebGL state has been modified externally

I am trying to integrate PixiJS with an existing custom WebGL engine. The existing custom engine is the host and handles control to PixiJS every frame. The existing custom engine configures the WebGL state to an "almost" default state and then calls into PixiJS; after PixiJS is done, the existing custom engine does a full reset of the WebGL state.
In code:
onFrame() {
resetWebGLStateToDefault(gl);
gl.bindFramebuffer(...)
gl.viewport(...)
thenWeUsePixiJSToDoSomeAdvancedStuff();
resetWebGLStateToDefault(gl);
}
My question
In thenWeUsePixiJSToDoSomeAdvancedStuff(), how can I tell PixiJS that the state is not what it used to be the previous time that it ran? Pretty much everything has been reset; PixiJS should assume that everything is default and I would also like to tell PixiJS what the current viewport and framebuffer are.
I tried Renderer.reset, StateSystem.reset, StateSystem.forceState but I guess that's not enough; PixiJS keeps assuming that some textures that it has set previously are still bound (they are not, the existing custom engine unbinds everything) and I get lots of [.WebGL-0x7fd105545e00]RENDER WARNING: there is no texture bound to the unit ?. Pretty much for all texture units, 1-15, except the first one.
Edit
It's probably worth mentioning that I am calling into the renderer directly; I think I need to because the existing custom engine owns the render loop. I am basically trying something like this, but I am getting the WebGL texture errors after the first frame.
renderer.reset();
renderer.render(sprite);
renderer.reset();
Edit
I tried the same thing with an autoStart: false application, and I get the same error.
pixiApp.renderer.reset();
pixiApp.render();
pixiApp.renderer.reset();
The issue appears to be that I was calling into PixiJS with a currently bound FBO; I fixed all my problems by creating a separate PIXI.RenderTexture, rendering there, and then compositing on top of my WebGL engine using a fullscreen quad.
// Create a render texture
const renderTexture = PIXI.RenderTexture.create(640, 360);
// Render with PixiJS
renderer.reset();
renderer.render(this.stage, renderTexture);
renderer.reset();
// Retrieve the raw WebGL texture
const texture = renderTexture.baseTexture._glTextures[renderer.texture.CONTEXT_UID].texture;
// Now composite on top of the other engine
gl.bindFramebuffer(gl.FRAMEBUFFER, theFramebufferWhereINeededPixiJSToRenderInTheFirstPlace);
gl.useProgram(quadProgram);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(u_Texture, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
gl.vertexAttribPointer(0, 2, gl.BYTE, false, 2, 0);
gl.enableVertexAttribArray(0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
gl.useProgram(null);
You may need to resize() the renderer and/or the render texture, depending on your actual setup.

Openlayers 3 style a secondary geometry

I've got a feature object with three geometries on it, main and two others. Is there a way of styling each one of the geometries on a styleFunction pass? If one is a point geometry and the other two are linestring geometries, how would I style all three in one styleFunction?
I've got as far as having the sub-geometries available and the styleFunction can switch them with a call to this.setGeometryName() but after that I'm a bit stumped - as the point geometry at this point has already been styled (there is an array of styles waiting to be sent back from the styleFunction).
Am I correct in thinking I can style the other two geometries as well, or should I have a separate layer with separate geometries and style them individually (this would add an overhead).
Add sub-geometries as named 'sets' of the feature (feature.set()), then call them via this.get('name of geometry') and place that into an ol.style.Style call as the geometry parameter, then you can style it individually for that style.
Assuming your subgeometry is set on the feature as subgeom, your styleFunction could look like this:
function(feature, resolution) {
return [
new ol.style.Style({
// main geometry style
}),
new ol.style.Style({
geometry: feature.get('subgeom'),
// subgeom style
})
]
}

Dimensions of ImageMarker

I am new to Vuforia SDK. I have an image which acts as a target. I want to place this image on to the Imagemarker. In real time the size of the Imagemarker varies. Is there any method where I can get the width and height of the Imagemarker so that the target image fits exactly on the Imagemarker?
Since you did not specify if you are using the Unity or native APIs I will assume you are using Unity.
This is how you would go about it using the Vuforia API, placing this in a script attached to your ImageTarget GameObject.
IEnumerator Start()
{
Vuforia.ImageTarget img = GetComponent<Vuforia.ImageTargetBehaviour>().ImageTarget;
// This is rounded of in the console display,
// so individual components are printed afterwards
Debug.Log(img.GetSize());
Debug.Log(img.GetSize().x);
Debug.Log(img.GetSize().y);
Debug.Log(img.GetSize().z);
}
Alternatively you can directly use the Bounds of the renderer.
void Start()
{
Renderer r = GetComponent<Renderer>();
Debug.Log(r.bounds.size.x);
Debug.Log(r.bounds.size.y);
Debug.Log(r.bounds.size.z);
}
Needless to say this is just a quick solution, depending on the situation you might want to use this at runtime dynamically create content.
Yes, you can.
While placing the Image on the Image Marker to the relative size you want it to be, and when you run it you'll see that the size of the image will be relative to the Marker you've placed it on.

Trying to Use Transform to Rotate a UIView in MonoTouch

I have a UIControl defined in which I have used the MonoTouch.CoreGraphics classes to draw some items in and have put the UIControl into a UIView through AddSubview. I'm trying to take the view and turn the whole thing to simulate something sort of like movement of the minute or second hand on a clock or a dial. I'm under the impression that I can do that with the Transform on the UIView.
The name of my UIView is container. I've tried:
container.Transform.Rotate(-0.78f);
I have also tried:
CGAffineTransform t = CGAffineTransform.MakeIdentity();
t.Rotate(-0.78f);
container.Transform = t;
I have also tried:
CGAffineTransform t = CGAffineTransform.MakeRotation(-0.78f);
container.Transform = t;
I have also tried this and other combinations of it:
UIView.BeginAnimations("rotate");
UIView.SetAnimationDuration(0.5);
container.Transform.Rotate((float)Math.PI);
UIView.CommitAnimations();
None of the above have had any impact on my display. It does not rotate or move in the slightest. All of the iOS related posts refer to CGAffineTransformRotate, but I can't find a Mono exact match for that and am assuming that is the equivalent of what I am doing above. Is there some other way I should be trying to make my view rotate?
You are on the right track. I have a large game under development and do this all over the place in my code. Here is the simplest example I can give you:
using MonoTouch.CoreGraphics;
float degrees = 15;
UIView aView = new UIView(new RectangleF(10,10,10,10));
aView.Transform = CGAffineTransform.MakeRotation(3.14159f * degrees / 180f);
That's it.
The examples are sparse, especially the fact that the MakeRotation function is in radians (hence the use of pi to convert to degrees).

Resources