PixiJS: Constantly updating PIXI.RenderTexture dumps browser memory - memory

There are two sprites, one is dynamic and second have to be constantly updated with first one through PIXI.RenderTexture()
let sprite = new PIXI.Sprite(), tmp = new PIXI.Sprite();
//some manipulations with these sprites
app.ticker.add((delta_) => {
...
updateTexture(w, h); //width is dynamic value
...
})
Here is updateTexture() function
function updateTexture(w_, h_){
let renderTexture = PIXI.RenderTexture.create({ width: w_, height: h_});
tmp.texture = sprite.texture;
app.renderer.render(tmp, renderTexture);
app.renderer.clear();
placeholder_.texture = renderTexture;
placeholder_.texture.update();
}
And it seems that let renderTexture = PIXI.RenderTexture.create({ width: w_, height: h_}); creates a new instance in memory dumping it every time up to browser crash.
app.renderer.clear() doesn't helps.
Any ideas for PixiJS version 6.5.5?

Related

Plotting waveform in circular path

Trying to plot file waveform in circular form and looking through the underlying EZAudio's EZAudioPlot class docs tells me this should be possible. However I can't quite wrap my head how to construct the 'points' argument in the linked method. Anyone done this and could point to correct way?
UPDATE:
Looking into this I think I've gotten fairly good grasp how EZAudioPlotting works. The actual waveform drawing is done on EZAudioPlotWaveformLayer which is just simple CAShapeLayer subclass. On creation of EZAudioPlot one EZAudioPlotWaveformLayer is created and reference stored on EZAudioPlot class waveformLayer property.
The actual waveform is CGPath on EZAudioPlotWaveformLayer class and to manipulate it it should go something like below.
// Setup audio data for the plot
let file = EZAudioFile(url: url)
guard let data = file?.getWaveformData(withNumberOfPoints: pointCount) else {return}
// Setup the plot
let waveform = EZAudioPlot()
waveform.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
waveform.gain = 10.0
// + any other property tweaking you need
//Get hold of the underlaying layer
let myLayer = waveform.waveformLayer!
// Create the default presentation for the waveforms as CGPath
let path = waveform.createPath(withPoints: waveform.points, pointCount: pointCount, in: EZRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)).takeRetainedValue()
// ??? Here somehow tweak the CGPath eg. into circular shape ???
newPath = plotPointsIntoCircleOrSomeOtherNiftyFunc(path)
// Stick new path to layer
myLayer.path = newPath
Only thing is that currently I haven't figured any math to actually implement anything meaningful at that pseudo code point

Unable to create a working sprite

I am unable to create a working animation sprite for this single area of my app.
I am using Corona SDK and have the following sprite:
This is names mainCharacter.png. I have a double sized version called mainCharacter#2x.png.
I have sheet options, 2 sequences, I'm building an image sheet and passing that to my sprite:
local playerSheetOptions =
{
width = 50,
height = 50,
numFrames = 17,
sheetContentWidth = 500,
sheetContentHeight = 100
}
local playerSequences = {
{
name = "idle",
start = 1,
count = 12,
time = 1200,
loopCount = 0,
loopDirection = "bounce"
},
{
name = "jump",
start = 13,
count = 5,
time = 600,
loopCount = 1
},
}
local playerSheet = graphics.newImageSheet( "resource/images/mainCharacter.png", playerSheetOptions )
local player = display.newSprite(gameSheet, playerSheet, playerSequences)
I am getting the following error:
display.newSprite() requires argument #2 to a table containing sequence data
If I print the relevant data:
print(gameSheet)
print(playerSheet)
print(playerSequences)
I get:
14:27:05.703 userdata: 12445228
14:27:05.703 userdata: 0CF42600
14:27:05.703 table: 0CF41FD0
Where am I going wrong? I have tried simplifying the sequences a lot, but still get the same thing.
Use
local player = display.newSprite(playerSheet, playerSequences)
instead of
local player = display.newSprite(gameSheet, playerSheet, playerSequences)
From Corona documentation
Once the image sheet(s) and sequences are set up, a new sprite object
can be created with the display.newSprite() API:
display.newSprite( [parent,] imageSheet, sequenceData )
For this API, the parent
parameter is optional and represents the display group in which to
insert the sprite. The imageSheet parameter defines the default image
sheet for the sprite, and sequenceData is the table that contains all
of the sequences for the sprite.
Read more about Sprite Animation.

Action not working correctly in SpriteKit

I'm new to iOS programing and I'm experimenting to learn trying to create a game in swift using Sprite Kit.
What I'm trying to achieve is having a constant flow of blocks being created and moving rightwards on the screen.
I start by creating a set which contains all the initial blocks, then an action "constant movement" is added to each one, which makes them move slowly to the right. What I'm having trouble is adding new blocks to the screen.
The last column of blocks has an "isLast" boolean set to true, when it passes a certain threshold it is supposed to switch to false and add a new column of blocks to the set which now have "isLast" set to true.
Each block in the set has the "constantMovement" action added which makes them move slowly to the right, the new blocks have it added as well, but they don't work as the original ones.
Not all of the move, even tho if I print "hasActions()" it says they do, and the ones that do move stop doing so when they get to the middle of the screen. I have no idea why this happens, can somebody experienced give me a hint please?
This is the update function:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let constantMovement = SKAction.moveByX(-1, y: 0, duration: 10);
background.runAction(SKAction.repeatActionForever(constantMovement));
let removeBlock = SKAction.removeFromParent();
let frame = self.frame;
var currentBlockSprite:SKSpriteNode;
var newBlock: Block;
for block in blocks {
currentBlockSprite = block.sprite!;
currentBlockSprite.runAction(constantMovement);
if(block.column == NumColumns - 1) {
block.isLast = true;
}
if(block.isNew) {
println("position \(currentBlockSprite.position.x) has actions \(currentBlockSprite.hasActions())");
}
if(block.isLast && currentBlockSprite.position.x < frame.maxX - 50) {
println("the block that hits is " + block.description);
println("HITS AT \(currentBlockSprite.position.x)");
block.isLast = false;
for row in 0..<NumRows {
newBlock = Block(column: NumColumns - 1, row: row, blockType: BlockType.random(), isLast: true, isNew: true);
blocks.addElement(newBlock);
addBlockSprite(newBlock);
println("new block: " + newBlock.description + "position \(newBlock.sprite?.position.x)");
}
}
if(currentBlockSprite.position.x < frame.minX) {
currentBlockSprite.runAction(removeBlock);
blocks.removeElement(block);
}
}
}
My whole project is in here: https://github.com/thanniaB/JumpingGame/tree/master/Experimenting
but keep in mind that since I'm new to this it might be full of cringeworthy bad practices.
I would remove any SKAction code from the update function as that's kind of a bad idea. Instead I would just apply the SKAction when you add your block sprite to the scene, like this.
func addBlockSprite(block: Block) {
let blockSprite = SKSpriteNode(imageNamed: "block");
blockSprite.position = pointForColumn(block.column, row:block.row);
if(block.blockType != BlockType.Empty) {
addChild(blockSprite);
let constantMovement = SKAction.moveByX(-10, y: 0, duration: 1)
var currentBlockSprite:SKSpriteNode
let checkPosition = SKAction.runBlock({ () -> Void in
if(blockSprite.position.x < -512){
blockSprite.removeAllActions()
blockSprite.removeFromParent()
}
})
let movementSequence = SKAction.sequence([constantMovement, checkPosition])
let constantlyCheckPosition = SKAction.repeatActionForever(movementSequence)
blockSprite.runAction(constantlyCheckPosition)
}
block.sprite = blockSprite;
}
That would then allow you to simply add a new block whenever you see fit and it will have the appropriate action when it's added.
I've used 512 as thats the size of the iPhone 5 screen but you could swap this out for another screen size or what would be better would be a variable that dynamically reflects the screen size.

UISlider not updating to bound ViewModel Property

I've got a UISeekbar and a UILabel located inside an MvxView
_currentPositionText = new UILabel { TextColor = UIColor.White };
_seekBar = new UISlider
{
MinValue = 0,
Continuous = true,
AutoresizingMask = UIViewAutoresizing.FlexibleWidth
};
_seekBar.TouchDown += OnTouchDown;
_seekBar.TouchUpInside += OnTouchUpInside;
_seekBar.TouchDragInside += OnDragInside;
I'm setting the binds as follows
set.Bind(_seekBar).For(sb => sb.Value).To(vm => _viewModel.CurrentPositionMsec);
set.Bind(_seekBar).For(sb => sb.MaxValue).To(vm => _viewModel.DurationMsec);
set.Bind(_currentPositionText).To(vm => vm.CurrentPositionText);
If I add some trace code inside the get of the CurrentPositionMsec, I can see the property updating once per second (as expected).
note: That same method also updates the CurrentPositionText by simply formatting the milliseconds into a TimeSpan.
The binding for the _currentPositionText is updating as expected, but the _seekBar doesn't update the slider position.
The expected result is for the slider to update once per second based on the CurrentPositionMsec.
I have this working in Android with the following binding, and both Android and iOS are sharing the same viewModel.
<SeekBar
android:Foo=""
android:Bar=""
local:MvxBind="Max DurationMsec; Progress CurrentPositionMsec" />
Am I not setting something up right? This "should" work AFAIK.
Reordering the bindings does not appear to yield any better results
// binding MaxValue first doesn't fix (lambda)
set.Bind(_seekBar).For(sb => sb.MaxValue).To(vm => _viewModel.DurationMsec);
set.Bind(_seekBar).For(sb => sb.Value).To(vm => _viewModel.CurrentPositionMsec);
Changing the binding to use a string rather than a lambda DID prove to solve the issue.
// using a string did enable the bindings to work as expected.
set.Bind(_seekBar).For("MaxValue").To(vm => _viewModel.DurationMsec);
set.Bind(_seekBar).For("Value").To(vm => _viewModel.CurrentPositionMsec);
We have now chosen to use strings as a standard in our code base. It does leave room for mistakes since we don't get compile time errors, but at least it ensures the bindings work.
I added a second SeekBar to the SeekView sample in https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/ApiExamples
This code seemed to work well:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var label = new UILabel(new RectangleF(10, 100, 100, 30));
label.Text = "Slide me:";
Add(label);
var seek = new UISlider(new RectangleF(110, 100, 200, 30));
seek.MinValue = 0;
seek.MaxValue = 100;
Add(seek);
var seek2 = new UISlider(new RectangleF(110, 160, 200, 30));
seek2.MinValue = 0;
seek2.MaxValue = 100;
Add(seek2);
var mirrorLabel = new UILabel(new RectangleF(110, 130, 200, 30));
mirrorLabel.TextColor = UIColor.Blue;
Add(mirrorLabel);
var set = this.CreateBindingSet<SeekView, SeekViewModel>();
set.Bind(seek).To(vm => vm.SeekProperty);
set.Bind(seek2).To(vm => vm.SeekProperty);
set.Bind(mirrorLabel).To(vm => vm.SeekProperty);
set.Apply();
}
Obviously this only exercises the current value and not the maximum - but hopefully getting this working will help.
My only suggestion might be to try reordering your bindings - so that MaxValue is set before Value is. Or perhaps try using a fixed MaxValue and then using a value converter (or other mechanism) to scale the current value.

How do I animate a sprite sheet using monotouch and core animation?

The code below is where I am right now, I've made it so I can click a button and change from one sprite on a sprite sheet to another. The fundamentals I'm working out may be crap, I don't really know what I'm doing! By all means, steer me straight if this is so.
What I want to know is how to loop over all the sprites in my sprite sheet. Currently I can just flip between the first and second one (only one time) via button click.
UIImageView test = new UIImageView();
test.Image = UIImage.FromFile("necromancerSpriteSheet.png");
CGImage img = test.Image.CGImage;
CALayer layer = new CALayer();
layer.Contents = img;
layer.Bounds = new RectangleF(0, 0, 128, 128);
layer.Position = new PointF(150, 250);
UIImageView theImage = new UIImageView();
layer.ContentsRect = new RectangleF(0, 0, 0.33f, 0.33f);
layer.RemoveAllAnimations();
theImage.Layer.AddSublayer(layer);
button.TouchUpInside += (s, e) => {
layer.ContentsRect = new RectangleF(0.33f, 0, 0.33f, 0.33f);
layer.RemoveAllAnimations();
theImage.Layer.AddSublayer(layer);
};
I would expect the process would be to have all the sublayers loaded/initialized then simply loop over them in some way. I haven't had any luck figuring out how to achieve this yet though.
Here is my test spritesheet if it helps...
There are many ways of solving this problem, but if you want to use CoreAnimation to drive the animation on the sheet, my suggestion is that you use a keyframe animation (CAKeyFrameAnimation) to drive the animation.
To simplify things, I would merely put all of the items in a single row, and then use something like:
var anim = (CAKeyFrameAnimation) CAKeyFrameAnimation.FromKeyPath ("position.x");
anim.Values = new NSNumber [9];
var times = new NSNumber [9];
for (int i = 0; i < 9; i++){
anim.Values [i] = NSNumber.FromFloat (i*WIDTH);
times [i] = NSNumber.FromFloat (i/9f);
}
anim.CalculationMode = CAKeyFrameAnimation.CalculationDiscrete
You might need to tweak the animation above a little, I did the above without testing.

Resources