Poll of the Day > Any intermediate programmers here?

Topic List
Page List: 1
jamieyello3
04/03/17 10:19:44 PM
#1:


I'm working on my C# XNA 2D animation studio. I've got an object for animation data, an object for image textures, and a drawing function that uses them interchangeably.

The problem is, whenever I want to add a new frame or limb, I have to recreate the entire object, due to arrays working the way they do. When I looped through and added old data to the new, it would create a reference to the data instead of just new data. No problem, I just create some local .Clone() functions that return copies.

Having done all that, it still is creating references instead of copies. If I try to edit the frame data of one frame, every single frame is edited. What can I do to fix this? Is there a hole in this somewhere?

https://github.com/jamieyello/Day_2D_Animation_Studio

By the way I intend on keeping this 100% free and open source, the game development scene was missing a really good simple animation editor. I found some crap ones and I found some $60 ones that didn't work.

Edit, Editor controls are right click to pan, and left click (drag and drop is buggy atm so it's a single click to pick up and place) to select images and move them around.
... Copied to Clipboard!
CountessRolab
04/03/17 10:28:24 PM
#2:


have you tried jiggling the handle?
---
... Copied to Clipboard!
jamieyello3
04/03/17 10:30:39 PM
#3:


CountessRolab posted...
have you tried jiggling the handle?

I'm more designing the toilet than trying to get the toilet to work.
... Copied to Clipboard!
Sahuagin
04/03/17 10:42:19 PM
#4:


havin a look...

I don't see an obvious reason why your clone methods wouldn't be working so far. do you have an example of why you think they aren't?
---
... Copied to Clipboard!
Sahuagin
04/03/17 10:58:03 PM
#5:


some random advice:

try to use same line braces instead of K&R. saves vertical space allowing you to read more code more easily.

for hooking up an event, note that this:


gameForm.DragEnter += new DragEventHandler(fmr_DragEnter);

...

private void frm_DragEnter(object sender, DragEventArgs e) {
// code
}


can be reduced to:

gameForm.DragEnter += (s, e) => {
// code
};


try to force yourself to have one class per file

seal a class if you have no reason to inherit from it

don't use public fields, use auto properties or properties with a private field and try to make the members readonly if possible

especially don't have public array fields. use a private list and then put add and remove methods on your class.

sorry if this just sounds like nagging or just makes things feel even more complex than they already do.

another one is, try not to write:

if (condition) {
// huge block of code
}


write:

if (!condition)
return;

// rest of code

---
... Copied to Clipboard!
GanonsSpirit
04/03/17 11:04:12 PM
#7:


jamieyello3 posted...
the game development scene was missing a really good simple animation editor

I usually just import/export animations and spritesheets into Game Maker to edit them. Its sprite editor is simple yet powerful.
---
http://i.imgur.com/tsQUpxC.jpg Thanks, Nade Duck!
[[[[[[[[[[[[[[[|||||||||||||]]]]]]]]]]]]]]]]
... Copied to Clipboard!
jamieyello3
04/03/17 11:16:42 PM
#8:


Sahuagin posted...
havin a look...

I don't see an obvious reason why your clone methods wouldn't be working so far. do you have an example of why you think they aren't?

https://www.youtube.com/watch?v=kQA4WW-B4Lw


First I added a couple blank frames with the + symbol (adding an image first crashes and I can fix that later)

And that gray bar down there is the frame selector. Pressing left and right will change the selected frame, and each frame is drawn...

Oh wow right now I'm seeing my clone method is working.. But for some reason it's not drawing correctly the right place. I just noticed I can move the highlighted back rectangle and that saves the data correctly.

Here I was bashing my head against the wrong place. I should be able to figure it out from here though, thanks for looking at it. I wouldn't have thought opening opening it and recording it would have led me to figuring it out.

Sahuagin posted...
some random advice:

Noted, I never took a class in any language up until VB.NET, so none of my coding habits are "proper", but I'm always looking to do things the best way.

I think the one thing that doesn't apply here though, I only want to make the end user have to use a single CS file that contains everything they need. That's why I have more than one class in the single file.

GanonsSpirit posted...
I usually just import/export animations and spritesheets into Game Maker to edit them. Its sprite editor is simple yet powerful.

The problem I have with engines is you never get to make it from scratch and you always spend more time learning the workflow at every step than it saves. Example being do I really want to learn GML when I can learn C#.

Not that it's bad it just isn't my thing. I tried UE4 for a while, and wow, it's like I need certification to use this.
... Copied to Clipboard!
Sahuagin
04/03/17 11:19:36 PM
#9:


jamieyello3 posted...
I think the one thing that doesn't apply here though, I only want to make the end user have to use a single CS file that contains everything they need. That's why I have more than one class in the single file.

what you would do is make a class library project and then you can reference it later from any program you make
---
... Copied to Clipboard!
Sahuagin
04/03/17 11:26:14 PM
#10:


you have a lot of Microsoft.Xna.Framework.Input.Keys, possibly because it's otherwise ambiguous. you can use a line like this to disambiguate:

using Keys = Microsoft.Xna.Framework.Input.Keys;

or if you need to use both the windows and XNA ones:


using WinKeys = System.Windows.Forms.Keys;
using XNAKeys = Microsoft.Xna.Framework.Input.Keys;

---
... Copied to Clipboard!
Sahuagin
04/03/17 11:33:18 PM
#11:


In AddFrame method of AnimationSetFull2D class you have these lines:

//Update animation data
AnimationSetClass tempAnimation = new AnimationSetClass(NumberOfLimbs, NumberOfFrames);
tempAnimation = (AnimationSetClass)Animation.Clone(0, 1);

Animation = (AnimationSetClass)tempAnimation.Clone();


you create a new AnimationSetClass, but then throw it away, copying the one in Animation, and then clone that one again back into Animation. This may (or may not) be related to your problem. This is roughly equivalent to something like:

Animation = Animation.Clone(0, 1);
or
Animation = Animation.Clone(0, 1).Clone();

The AddLimb method has a similar sequence.
---
... Copied to Clipboard!
jamieyello3
04/03/17 11:56:51 PM
#12:


I didn't actually realize AnimationSetFull contained all the other classes, I fixed that.

Also fixed the double cloning, I added that in thinking maybe if I double cloned it that would solve my problem somehow.

I know I have public array fields in some of my classes, but I wanted the end user to be able to access the data if they really wanted to get into it. I'm not familiar with lists, but I can already see the headache of working with arrays that are supposed to change in size during runtime. Maybe once I get it set up and working I'll come back and change that.
... Copied to Clipboard!
Magus 10
04/04/17 1:08:05 AM
#13:


jamieyello3 posted...
I'm not familiar with lists, but I can already see the headache of working with arrays that are supposed to change in size during runtime. Maybe once I get it set up and working I'll come back and change that.


Indeed, it looks to me like you're doing a lot of work to allocate new arrays and copy everything from the old array to the new array whenever you want to add things to the array.

Lists are much simpler.

var list = new List<TypeOfThingToHaveAListOf>();

list.add(new TypeOfThingToHaveAListOf());


Also, I think your bug is that in DrawFrameSelector, you iterate over each frame and call CurrentAnimationSet.Draw for that frame, but inside AnimationSetFull2D, you don't have a separate list of textures per frame.

Inside AnimationSetFull2D.Draw:

for (int limb = 0; limb < NumberOfLimbs; limb++)
{
LocalSpriteBatch.Draw(
Textures.Limb[limb], // Texture
Animation.Frame[frame].Limb[limb].Position, // Position
null, // Previously sourceRectangle
Animation.Frame[frame].Limb[limb].Colour, // Color
Animation.Frame[frame].Limb[limb].Rotation, // Rotation
Animation.Frame[frame].Limb[limb].Origin, // Origin
Animation.Frame[frame].Limb[limb].Scale, // Scale
Animation.Frame[frame].Limb[limb].SpriteEffect,
Animation.Frame[frame].Limb[limb].Layer);
}


So once you add a texture, that same texture will be drawn for every frame.

I could be wrong though.
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
GanonsSpirit
04/04/17 3:44:51 AM
#14:


jamieyello3 posted...
The problem I have with engines is you never get to make it from scratch and you always spend more time learning the workflow at every step than it saves. Example being do I really want to learn GML when I can learn C#.

I'm not saying use Game Maker, just its sprite editor.
---
http://i.imgur.com/tsQUpxC.jpg Thanks, Nade Duck!
[[[[[[[[[[[[[[[|||||||||||||]]]]]]]]]]]]]]]]
... Copied to Clipboard!
jamieyello3
04/04/17 4:40:27 AM
#15:


Actually that's working as intended (for all I know), I don't want a seperate texture for every frame. Plus the texture itself has no say in where it's drawn.

GanonsSpirit posted...
jamieyello3 posted...
The problem I have with engines is you never get to make it from scratch and you always spend more time learning the workflow at every step than it saves. Example being do I really want to learn GML when I can learn C#.

I'm not saying use Game Maker, just its sprite editor.

I use Artrage to draw all my textures personally. As for actually animating them, I'm aiming for a modern skelectal animation that creates interpolation frames in between. My editor is actually really close to having something to show for all the work, tomorrow I'll get back at it and try and put it all together. Maybe not, I might be too tired.

To draw the frames you'd need some kind of plugin, so unless GameMaker outputs an XNA library to use the files with I don't think Gamemaker will be of any use.

If I ever need sprite graphics I'll check it out.
... Copied to Clipboard!
EightySeven
04/04/17 5:31:31 AM
#16:


Protip: if it's a language specific question put the language you're using in the title.

Example: Any intermediate C# programmers here?
... Copied to Clipboard!
Magus 10
04/05/17 12:22:39 AM
#17:


jamieyello3 posted...
Actually that's working as intended (for all I know), I don't want a seperate texture for every frame. Plus the texture itself has no say in where it's drawn.


Ah, maybe I misunderstood the problem. It's not that after you add the image it shows up in every frame, it's that the position data is the same for every frame?

Edit: Ignore everything below here; I'm wrong. Sahuagin corrected me below.

Taking another look, I see that Position and Origin in SingleLimbAnimationState are both references:

public Vector2 Position = new Vector2(0, 0);
public Vector2 Origin = new Vector2(0, 0);


Inside SingleLimbAnimationState.Clone() you're copying the references to the Position and Origin:

public object Clone()
{
SingleLimbAnimationState C = new SingleLimbAnimationState();
C.InterpolationType = InterpolationType;
C.Rotation = Rotation;
C.Scale = Scale;
C.Name = Name;
C.Colour = Colour;
C.SpriteEffect = SpriteEffect;
C.Layer = Layer;
C.Position = Position;
C.Origin = Origin;

return C;
}


So if you're updating the Limb Position directly, since any cloned Limbs would reference the same Position, they would all get updated.

You'd want to do something like C.Position = new Vector2(C.X, C.Y);, and the same thing with Origin.
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
Sahuagin
04/05/17 12:33:24 AM
#18:


Magus 10 posted...
You'd want to do something like C.Position = new Vector2(C.X, C.Y);, and the same thing with Origin.

Vector2 is a struct not a class, it has value semantics (note that struct in C# does not mean what it means in C/C++)
---
... Copied to Clipboard!
Magus 10
04/05/17 12:40:17 AM
#19:


Sahuagin posted...
Magus 10 posted...
You'd want to do something like C.Position = new Vector2(C.X, C.Y);, and the same thing with Origin.

Vector2 is a struct not a class, it has value semantics


Today I learned.

https://msdn.microsoft.com/en-us/library/aa664472(v=vs.71).aspx

I guess I'm not used to the less common parts of C#.

My bad.
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
Sahuagin
04/05/17 12:50:18 AM
#20:


np

one of the trickier problems you get from structs having value semantics is what happens if you allow them to be mutable, and then expose one as a property. ex:


class SomeClass {
...
public Vector2 SomeProperty { get; set; }
...
}

...
var someObject = new SomeClass();
someObject.SomeProperty.X = 5; // oops?


since SomeProperty returns a copy of the value and not an object, assigning a value to the property of the copied value does nothing.
---
... Copied to Clipboard!
Magus 10
04/05/17 12:57:07 AM
#21:


Sahuagin posted...
since SomeProperty returns a copy of the value and not an object, assigning a value to the property of the copied value does nothing.


That sounds fun.
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
Sahuagin
04/05/17 1:19:50 AM
#22:


Magus 10 posted...
That sounds fun.

rule of thumb is to always make structs immutable. (basically in C# a struct is a "user primitive", so should be thought of as immutable values anyway; you don't change properties of an integer for example. (immutability is just a good thing in general as well.))

for some reason they didn't follow their own advice with Vector2 (maybe for performance reasons...?). OP would probably have encountered this problem, but he's using public fields which avoids it since he's accessing the field directly. (though I haven't been completely through his code yet so it may be lurking there somewhere)

it gets annoying though, because to properly change X as above you have to do

someObject.SomeProperty = new Vector2(5, someObject.SomeProperty.Y);


which is a pain. solution I usually use is to write a few extension methods and then it's not so bad:

public static Vector2 SetX(this Vector2 pVector2, float pX) {
return new Vector2(pX, pVector2.Y);
}

and then you can at least write:

someObject.SomeProperty = someObject.SomeProperty.SetX(5);

---
... Copied to Clipboard!
Magus 10
04/05/17 1:37:02 AM
#23:


Okay, so I think I may finally have an idea of what's going on.

I'm not very familiar with the semantics of XNA/monogame, but as far as I can tell, the problem has to do with using a single RenderTarget2D for every frame.

I spent some time playing around, and it seems that the last frame that gets drawn to the render target is what gets rendered to every location where that render target is used (i.e. each of the Frame Selectors, as well as the 'Project' draw call).

Adding this line to the beginning of the AnimationSetFull2D.Draw method "fixes" that problem (I say "fixes" because you end up allocating memory every frame, and eventually the process slows to a crawl, so this is just a proof of concept).

public void Draw(GraphicsDevice graphics, ref SpriteBatch spriteBatch, float Frame, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, float scale = 1f, SpriteEffects effects = SpriteEffects.None, float layerDepth = 0f)
{
var renderTarget = new RenderTarget2D(
graphics,
graphics.PresentationParameters.BackBufferWidth,
graphics.PresentationParameters.BackBufferHeight,
false,
graphics.PresentationParameters.BackBufferFormat,
DepthFormat.Depth24);

...


Maybe each frame needs its own RenderTarget2D object?

Hopefully that helps pinpoint the solution.
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
Sahuagin
04/05/17 1:56:00 AM
#24:


I think you may just be blocking the issue from being visible by doing that (whoa does that ever eat memory fast).

He seems to be drawing all images to all frames, instead of each image to its own frame. (if you add two different images, they both appear on both frames). but I can't tell exactly where or what it is causing it because the code is so messy and I'm too tired. if it manages to remain unsolved till the weekend I will probably tackle it on saturday.
---
... Copied to Clipboard!
Sahuagin
04/05/17 2:25:47 AM
#25:


ok, I went through it a bit more even though I should be sleeping.

you're kind of right.

what it is is that spritebatch queues up its draws. he calls LocalSpriteBatch.End();, which processes all those calls to the render target, but he then draws the rendertarget with the other spritebatch and keeps that one running. he queues up all the rendertarget draws that way, but he destroys the rendertarget each time before the spritebatch ends. when he finally calls end on the outer spritebatch, it runs through its queue and draws only the rendertarget in its final state in each case.

this needs to be fixed by calling end and begin on the spritebatch more often. (it would also be fixed by, like you said, using separate render targets). for example, if you do this at the bottom of that draw method:

spriteBatch.End();
spriteBatch.Begin();
spriteBatch.Draw(renderTarget, position, sourceRectangle, Color.White, rotation, origin, scale, effects, layerDepth);
spriteBatch.End();
spriteBatch.Begin();


it fixes the problem (because the spritebatch now draws the render target immediately), although some of the other graphics get lost (mostly the central area).

it's good to keep in mind that calling SpriteBatch.Draw does not actually draw anything, but rather *enqueues* a draw operation. it's calling *End* that actually does the drawing and if too much stuff has changed in the mean time, stuff like this happens.

gonna go to bed now...
---
... Copied to Clipboard!
Magus 10
04/05/17 2:28:09 AM
#26:


Sahuagin posted...
I think you may just be blocking the issue from being visible by doing that (whoa does that ever eat memory fast).

He seems to be drawing all images to all frames, instead of each image to its own frame. (if you add two different images, they both appear on both frames). but I can't tell exactly where or what it is causing it because the code is so messy and I'm too tired. if it manages to remain unsolved till the weekend I will probably tackle it on saturday.


As far as I can tell, renderTarget is a texture that gets written to by the calls to LocalSpriteBatch.Begin/Draw/End, then that texture is passed to spriteBatch.Draw. That Draw call stores a reference to the texture, which actually gets rendered when spriteBatch.End is called (after the call to DrawUI is completed).

So he's telling spriteBatch to render the same texture in several different places. By the time End is called, it iterates through each place it was told to Draw and draws the texture (which at this point is not changing) at each one.

By using a separate texture for each frame, you do what you actually expect and render what you drew during the call to AnimationSetFull2D.Draw.
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
Magus 10
04/05/17 2:30:05 AM
#27:


Sahuagin posted...
ok, I went through it a bit more even though I should be sleeping.

you're kind of right.

what it is is that spritebatch queues up its draws. he calls LocalSpriteBatch.End();, which processes all those calls to the render target, but he then draws the rendertarget with the other spritebatch and keeps that one running. he queues up all the rendertarget draws that way, but he destroys the rendertarget each time before the spritebatch ends. when he finally calls end on the outer spritebatch, it runs through its queue and draws only the rendertarget in its final state in each case.

this needs to be fixed by calling end and begin on the spritebatch more often. (it would also be fixed by, like you said, using separate render targets). for example, if you do this at the bottom of that draw method:

spriteBatch.End();
spriteBatch.Begin();
spriteBatch.Draw(renderTarget, position, sourceRectangle, Color.White, rotation, origin, scale, effects, layerDepth);
spriteBatch.End();
spriteBatch.Begin();


it fixes the problem (because the spritebatch now draws the render target immediately), although some of the other graphics get lost (mostly the central area).

gonna go to bed now...


Oh you beat me to it. :(

Yeah, that's another option than using separate textures for each frame.
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
Sahuagin
04/05/17 2:46:39 AM
#28:


Magus 10 posted...
So he's telling spriteBatch to render the same texture in several different places. By the time End is called, it iterates through each place it was told to Draw and draws the texture (which at this point is not changing) at each one.

yeah you got it. I only got it because you identified the method and rendertarget thing first.

thanks for the fun times, I'm the only programmer I know IRL so it's nice to have threads like this every now and then
---
... Copied to Clipboard!
jamieyello3
04/05/17 11:07:05 PM
#29:


I'll just say there was no way I was ever narrowing this bug down to the render target without help, so thanks to both of you.

I added

spriteBatch.End();
spriteBatch.Draw(...);
spriteBatch.Begin();

Everywhere where there was a drawing call. That broke everything and errors that had nothing to do with drawing were popping up, so I undid that. There's a good chance I just did it wrong, so I'll keep that in mind and maybe try it again later.

So I gave each frame a render target, the function didn't draw, but the UI didn't go black when I added an image. Edit (I found out I was doing it wrong again)

And I say it's just as well, the whole reason my program is breaking is because I'm trying to draw the same object multiple times in a frame, and that's functionality I can live without right now. Each frame having a render target would be a really sloppy solution, since I'm doing i-frames, or generated frames in between frames. I can't reasonably have a frame for every single i-frame. I can just create copies of the AnimationSetFull's and draw those if I need to, then mark it up as needing optimization later. I'll create a separate .Draw() method that won't draw to any buffer if the end user wants to get into it for the time being. I think I'll make an overload method for a referenced render target.

I also organized the code a lot, I put all the classes in separate files and removed all public tags from my classes, I also renamed all the classes, and I did all the changes mentioned to make the code cleaner. I synced it all with GitHub as well. Now that it finally "works" I can get into adding editor functions, and I'll do something else with the frame select view.

Thanks again for the help, and I have to say, doing it this way makes things much easier. I'm finding lots of weird quirks in my code doing this. Now that it's working I can go back and clean it up a lot more.
... Copied to Clipboard!
Magus 10
04/06/17 12:19:41 AM
#30:


jamieyello3 posted...
Thanks again for the help, and I have to say, doing it this way makes things much easier. I'm finding lots of weird quirks in my code doing this. Now that it's working I can go back and clean it up a lot more.


Happy to help. :)
---
Internet = Tube0 + Tube1X + Tube2X^2/2! + Tube3X^3/3! + Tube4X^4/4! + ...
... Copied to Clipboard!
Sahuagin
04/06/17 8:42:49 PM
#31:


jamieyello3 posted...
I added

spriteBatch.End();
spriteBatch.Draw(...);
spriteBatch.Begin();


well... that's obviously going to fail the way that's written. begin has to be called before draw is called. it would need to be "end begin draw end begin" to inject it into the middle of an already running spritebatch, but that wasn't really meant as a solution, just as an indication of the problem.

there's a lot of different ways to organize the draw calls and textures, and I'm not sure I even know what the "best" way would be. either you have to wrap more of your draw calls in shorter-term begins and ends, or you have to use more surfaces.
---
... Copied to Clipboard!
jamieyello3
04/09/17 4:20:51 AM
#32:


^Typo, I meant the other way around.

I actually believe it was made to be used only once or twice a frame anyway, since it caches the instructions in spriteBatch.Draw() and doesn't actually draw them until the .End() function is called. It's supposedly most optimized when you draw everything at once.

I found a ton of nonsense crap in my code making bugs. Coding while tired isn't good. Right now the program is almost functional, except on going to tackle i-frames I realize I'll have to come up with a more complex editor than I wanted. I'm determined to keep it simple yet powerful, so I'll try to think of an innovative design. People design these things for programmers but they forget programmers have to waste their time learning software the same as anyone else.

The UI is pretty good looking though, I have to say.
... Copied to Clipboard!
Topic List
Page List: 1