HaxeFlixel / flixel-addons

Additional classes for HaxeFlixel
169 stars 139 forks source link

FlxShape API design caution!! #240

Open buckle2000 opened 8 years ago

buckle2000 commented 8 years ago

Check This First:: https://github.com/HaxeFlixel/flixel-addons/blob/dev/flixel/addons/display/shapes/FlxShape.hx

Main Problem

I know this will break API capability. But the earlier it is fixed, less problem will appear in the future!

For several reasons, FlxShape should not be like it is now. e.g. it is not good for MVC separation.

  1. FlxShape should NOT be a subclass of FlxSprite. But drawing a shape becomes a problem. See Questions below.
  2. A class FlxPolygon (or named FlxPoly or else), which is a subclass of FlxShape should be placed. Not all shapes are polygons! e.g. Line,
  3. FlxCircle should not be child of FlxShape, but of FlxPolygon. For some seldom later use, a new class FlxFastCircle may take place, which can only be drawn, and does not have features of FlxPolygon, which will be discussed later.
  4. All classes in flixel.addons.display.shape except FlxShape shall be removed!
    Instead,
    _==== UPDATED ====_ on 2016.3.15 0:28 UTC+8 Put some static methods in _FlxPolygon_ (simple shapes like box, triangle) or FlxShapeUtil(complex shapes like FlxShapeDoubleCircle).
    Methods in FlxShape will generate a FlxShape(basically a FlxPolygon).
    Methods in FlxShapeUtil will generate a FlxShapeGroup(just like FlxTypedGroup<FlxShape>, but is a subclass of FlxShape, with additional implements for easy printing and etc..
    More classes = more mess.
  5. All of the things mentioned above should be put in flixel.shape or flixel.util(FlxShapeUtil).

    I believe that it is important enough to be moved into main Flixel distributed package!

After you agree with this change, I will add the class FlxPolygon and when I have time.

FlxPolygon

FlxPolygon will have following features:

I have some questions and unmade decisions. This proposal also has some possible disadvantages.

Gama11 commented 8 years ago

We're trying to limit breaking changes to major releases, and we just had one of those.

buckle2000 commented 8 years ago

So how about the next release?

larsiusprime commented 8 years ago

Hey there :) I was the one who wrote these classes originally and I use them extensively in Defender's Quest.

I suppose they could be refactored, but like Gama says we're trying to limit breaking changes, so anything that we do here, especially if it goes into flixel core, would need to be delayed until the next point release. I mean eventually we'll have to start talking about what's going to go into the next major point release and start a branch working towards that and all, so if we do agree to move forward on these changes it might be a while before they make it into a release, until then they'd live on a branch.

As for the actual points you brought up, seems like it could clean some things up but I want to make sure I'm clear on what the benefits are.

To start, the original design was based on the paradigm that a FlxShape should be an easy to use geometric object where the size and boundaries of the object matches the underlying shape, rather than just be a raw pixel canvas with a shape on it -- ie, if you have a 10x10 square with a line thickness of 4, it's flixel bounding box is 10x10, not 14x14. This is accomplished by drawing the shape and setting bounding box offsets. This way if the user wants such a square at X,Y, they just put it there rather than constantly doing X+myshape.linethickness/2, etc.

So my understanding is you want to change things so that instead of having a class for each shape, you make a sort of factory pattern out of FlxShape static functions, and then those just return FlxSprites with all the proper stuff done to them? If you need to change the radius of a circle or update its line thickness would you just pass your circle instance back to the FlxShape circle function with new parameters? What does your proposed workflow look like?

buckle2000 commented 8 years ago

@larsiusprime @Gama11 glad to meet you too!

Explanations of FlxShape and so on

FlxShape

FlxShape is an abstract class, basically an interface between subclasses and {FlxSprite and other users}.

FlxShape should not extend anything (as far as I see), it even should never implement IFlxDestroyable.

They should not have properties like line thickness, fill color or something else. These properties should be defined just in the time before drawn, and should be passed in as arguments.

Just think it as a geometry.

Every FlxShape should has a way to be drawn!

There are 2 possible ways (or more?) to draw a FlxShape on a sprite:

  1. Put a static method in FlxSpriteUtil.

    Disadvantage: has even no extendibility to people that do not want to change haxeflixel code.

    Also, a lot of Std.is(shape, FlxPolygon) will be written, for sure.

  2. As I mentioned before:

Let FlxShape have draw(graphics, some arguments). But what type should graphics be? Perhaps flash.display.Graphics?

Receiving attributes like border width will still be a pain. Suggestions:

FlxPolygon

Same as its name, it is a polygon, but still a shape.

polygons have vertices: var vertices:Array<FlxPoint>

circles are polygons.

Since it has vertices, you can:

How can a user use these classes?

using ..FlxSpriteUtil; // for sprite.drawShape(...)
import ..FlxPolygon;
import flixel.FlxSprite;
import flixel.math.FlxPoint;

class SomeClass {
    var sprite: FlxSprite; // just pretend that it is pre-initialized
    function somefunc() {
        var a_circle = FlxPolygon.ellipse(radius, radius, center_position);

        var format = SomeShapeFormat(linewidth, fillcolor, etc...);
        sprite.drawShape(a_circle, format); // how we draw

        a_circle.scale(scale_on_x, scale_on_y); // resizing
        a_circle.rotate(angle_in_radians); // resizing
        a_circle.applyMatrix(matrix); // advanced use: transfomation matrix. other methods like shape.scale should call this method.

        var a_sqaure = FlxPolygon.regular_polygon(4, sidewidth);

        var vertices = [];
        vertices.push(new FlxPoint(0, 0));
        vertices.push(new FlxPoint(0, 10));
        vertices.push(new FlxPoint(20, 30));
        vertices.push(new FlxPoint(50, 90));

        var a_custom_polygon = new Polygon(vertices); // notice that a polygon is a close shape. so (50,90) and (0,0) should be connected when drawn.

        var a_strange_shape=FlxPolygon.intersect(a_custom_polygon,a_circle); // get intersection

        var overlaps:Bool=FlxPolygon.overlaps(a_custom_polygon,a_circle); // test overlapping
    }
}

:TODO:

buckle2000 commented 8 years ago

Also the first comment of this issue is updated.

I think its better to put helper methods in FlxPolygon.