Question

I am building a puzzle game in winforms, and i want to recognize the mousedown over any piece, and move it with the mousemove. The issue is that when i touch the transparent part of the puzzle piece, i want to verify if there is any piece behind that one, and if so, start the mousemove of that other piece, got it?

I am also able to recognize if the mousedown was over the image, or if it happens in the transparent part of the puzzle piece. My problem is to get the best way to pass the mouse event to the piece behind.

Many Thanks in advance.

UPDATE 1

The following is the class for the puzzle piece:

class Peça : DrawingArea
{
    private Point _Offset = Point.Empty;
    public Image imagem
    {
        get;
        set;
    }


    protected override void OnDraw()
    {
        Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
        this.graphics.DrawImage(imagem, location);
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (_Offset != Point.Empty)
        {
            Point newlocation = this.Location;
            newlocation.X += e.X - _Offset.X;
            newlocation.Y += e.Y - _Offset.Y;
            this.Location = newlocation;
        }
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        _Offset = Point.Empty;
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        Down(e);
        //Console.WriteLine(color.ToString());
    }

    public void Down(MouseEventArgs e)
    {
        Bitmap b = new Bitmap(imagem);
        Color? color = null;
        try
        {
            color = b.GetPixel(e.X, e.Y);
            if (color.Value.A != 0 && color != null)
            {
                if (e.Button == MouseButtons.Left)
                {
                    _Offset = new Point(e.X, e.Y);
                    this.BringToFront();
                }
            }
        }
        catch {
             }
    }
} 

The following code, is my DrawingArea (Panel) that i create in order to work with transparency:

abstract public class DrawingArea : Panel
{
    protected Graphics graphics;
    abstract protected void OnDraw();

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT

            return cp;
        }
    }

    public DrawingArea()
    {

    }

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {

    }

    protected override void OnPaint(PaintEventArgs e)
    {

        this.graphics = e.Graphics;


        this.graphics.TextRenderingHint =
            System.Drawing.Text.TextRenderingHint.AntiAlias;
        this.graphics.InterpolationMode =
            System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
        this.graphics.PixelOffsetMode =
            System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        this.graphics.SmoothingMode =
            System.Drawing.Drawing2D.SmoothingMode.HighQuality;


        OnDraw();
    } 
}

And you can also see my Form code:

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams handleParam = base.CreateParams;
            handleParam.ExStyle |= 0x02000000;       
            return handleParam;
        }
    }
}

Those are my pieces, and when i touch the transparent space in the first piece, i want to pick up the second one and move it on mouseMouse instead of doing nothing...

It looks like this:

enter image description here

Apologize my bad english.

UPDATE 2

I think i am getting very close to the solution, but something strange happens now, when i touch the piece behind another one, it disappear... What am i doing wrong?

SOME CODE UPDATES

Piece Class:

class Peça : DrawingArea
{
    private Point _Offset = Point.Empty;
    public Boolean movable = false;
    public Image imagem
    {
        get;
        set;
    }

    protected override void OnDraw()
    {
        Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
        this.graphics.DrawImage(imagem, location);
    }

    public void Move(MouseEventArgs e)
    {
        if (_Offset != Point.Empty)
        {
            Point newlocation = this.Location;
            newlocation.X += e.X - _Offset.X;
            newlocation.Y += e.Y - _Offset.Y;
            this.Location = newlocation;
        }
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        _Offset = Point.Empty;
        movable = false;
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        Down(e);
        //Console.WriteLine(color.ToString());
    }

    public Boolean Down(MouseEventArgs e, bool propaga=true)
    {
        Form parentForm = (this.Parent as Form);
        Bitmap b = new Bitmap(imagem);
        Color? color = null;
        Boolean flag = false;
        try
        {
            color = b.GetPixel(e.X, e.Y);
            if (color.Value.A != 0 && color != null)
            {
                if (e.Button == MouseButtons.Left)
                {
                    _Offset = new Point(e.X, e.Y);
                    this.BringToFront();
                    flag = true;
                    movable = true;
                }
            }
            else
            {
                if(propaga)
                (this.Parent as Form1).propagaEvento(this, e);
                flag = false;

            }
            return flag; 
        }
        catch {
            return flag; }
    }
}

Form1:

public partial class Form1 : Form
{
    private List<Peça> peças;
    private Point _Offset = Point.Empty;
    public Form1()
    {
        InitializeComponent();

        peças = new List<Peça>();
        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
        criaListaPecas();
        associaEventosPecas();      
    }

    private void associaEventosPecas()
    {
        foreach (Peça p in peças)
        {
            p.MouseMove += Form1_MouseMove;
        }
    }

    private void criaListaPecas()
    {
        peças.Clear();
        foreach (Control p in this.Controls)
        {
            if (p.GetType() == typeof(Peça))
                peças.Add((Peça)p);
        }
        Console.WriteLine(peças[0].Name);
        Console.WriteLine(peças[1].Name);
        Console.WriteLine(peças[2].Name);
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams handleParam = base.CreateParams;
            handleParam.ExStyle |= 0x02000000;       
            return handleParam;
        }
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        label1.Text = e.Location.ToString();
        gereMovimento(e);
    }

    private void gereMovimento(MouseEventArgs e)
    {
        foreach (Peça p in peças)
        {
            if (p.movable)
            {
                p.Move(e);
            }
        }
    }

    internal void propagaEvento(Peça peça, MouseEventArgs e)
    {
        foreach (Peça p in peças)
        {
            if (p != peça)
            {
                if (p.Down(e, false))
                    break;
            }
        }
    }
}

Thanks in advance again :)

Was it helpful?

Solution 3

SOLVED :)

i have figured it out... Here's the code to anybody how needs (I have made it right now, so the code is not clean yet):

Piece Class:

class Peça : DrawingArea
{
    private Point _Offset = Point.Empty;
    public Boolean movable = false;

    public Image imagem
    {
        get;
        set;
    }

    protected override void OnDraw()
    {
        Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
        this.graphics.DrawImage(imagem, location);
    }

    public Boolean Down(Point e, bool propaga = true)
    {
        Bitmap b = new Bitmap(imagem);
        Color? color = null;
        Boolean flag = false;
        try
        {
            color = b.GetPixel(e.X, e.Y);
            if (color.Value.A != 0 && color != null)
            {
               flag = true;
            }
            else
            {
                flag = false;
            }
            return flag;
        }
        catch
        {
            return flag;
        }
    }
}

Form1:

public partial class Form1 : Form
{
    private List<Peça> peças;
    private Point _Offset = Point.Empty;
    private Peça peça1, peça2, peça3, peça4;
    private bool canMove;
    private Peça atual;
    private bool other=false;
    public Form1()
    {
        FormBorderStyle = FormBorderStyle.None;
        WindowState = FormWindowState.Maximized;
        InitializeComponent();

        atual = new Peça();

        peça1 = new Peça();
        peça2 = new Peça();
        peça3 = new Peça();
        peça4 = new Peça();
        peça1.imagem = Properties.Resources._4p1_1;
        peça2.imagem = Properties.Resources._4p1_2;
        peça3.imagem = Properties.Resources._4p1_3;
        peça4.imagem = Properties.Resources._4p1_4;

        peças = new List<Peça>();

        peça1.Name = "peça1";
        peça2.Name = "peça2";
        peça3.Name = "peça3";
        peça4.Name = "peça4";

        this.Controls.Add(peça1);
        this.Controls.Add(peça2);
        this.Controls.Add(peça3);
        this.Controls.Add(peça4);

        criaListaPecas();

        foreach (Peça p in peças)
        {
            p.Size = new Size(p.imagem.Width, p.imagem.Height);
        }

        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);

        associaEventosPecas();
        canMove = false;
    }

    private void associaEventosPecas()
    {
        foreach (Peça p in peças)
        {
            p.MouseMove += Form1_MouseMove;
            p.MouseDown += Form1_MouseDown;
            p.MouseUp += Form1_MouseUp;
        }
    }

    private void criaListaPecas()
    {
        peças.Clear();
        foreach (Control p in this.Controls)
        {
            if (p.GetType() == typeof(Peça))
                peças.Add((Peça)p);
        }
        Console.WriteLine(peças[0].Name);
        Console.WriteLine(peças[1].Name);
        Console.WriteLine(peças[2].Name);
        Console.WriteLine(peças[3].Name);
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams handleParam = base.CreateParams;
            handleParam.ExStyle |= 0x02000000;
            return handleParam;
        }
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (sender.GetType().Equals(typeof(Peça)))
        {
            label1.Text = new Point(e.Location.X + (sender as Peça).Location.X, e.Location.Y + (sender as Peça).Location.Y).ToString();
        }
        else
        label1.Text = e.Location.ToString();
        gereMovimento(sender, e);
    }

    private void gereMovimento(object sender, MouseEventArgs e)
    {
        if (canMove)
        {
            if (other)
            {
                Point p = atual.PointToClient(new Point(e.X + (sender as Peça).Location.X, e.Y + (sender as Peça).Location.Y));

                Point newlocation = atual.Location;
                newlocation.X += p.X - _Offset.X;
                newlocation.Y += p.Y - _Offset.Y;
                atual.Location = newlocation;
            }
            else
            {
                Point newlocation = atual.Location;
                newlocation.X += e.X - _Offset.X;
                newlocation.Y += e.Y - _Offset.Y;
                atual.Location = newlocation;
            }
        }
    }

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        if (sender.GetType().Equals(typeof(Peça)) && e.Button == MouseButtons.Left)
        {
            atual = sender as Peça;
            atual.BringToFront();
            criaListaPecas();
            if (atual.Down(e.Location))
            {
                _Offset = new Point(e.X, e.Y);
                canMove = true;
                other = false;
            }
            else
            {
                Console.WriteLine(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)));
                Console.WriteLine(atual.Location);
                Point p = new Point(); 
                if (peças[1].ClientRectangle.Contains(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
                    && peças[1].Down(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
                {
                    p = peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
                    atual = peças[1];
                    atual.BringToFront();
                    criaListaPecas();
                    _Offset = p;
                    canMove = true;
                    other = true;
                }
                else if (peças[2].ClientRectangle.Contains(peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
                    && peças[2].Down(peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
                {
                    p = peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
                    atual = peças[2];
                    atual.BringToFront();
                    criaListaPecas();
                    _Offset = p;
                    canMove = true;
                    other = true;
                }
                else if (peças[3].ClientRectangle.Contains(peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
                    && peças[3].Down(peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
                {
                    p = peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
                    atual = peças[3];
                    atual.BringToFront();
                    criaListaPecas();
                    _Offset = p;
                    canMove = true;
                    other = true;
                }
            }
        }
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        canMove = false;
    }
}

Apologize the repeated and confused code, but as i said, i have made it seconds ago, and did not clean the code yet ;)

OTHER TIPS

Pieces can be represent as:

public class Piece
{
    public Point Location {get; set;}
    public int Z {get; set;}
    public int ID {get; set;} // to be bound to control or a control itself?
    public Image Image {get; set;} // texture?
    public DockStyle PlusArea {get; set;}
    public DockStyle MinusArea {get; set;}  // can be None
    ...

    public bool HitTest(Point point)
    {
        // assuming all of same size
        if((new Rectangle(Location, new Size(...)).Contains(point))
        {
            switch(MinusArea)
            {
                case Top:
                    if((new Rectangle(...)).Contains(point))
                        return false;
                ...
            }
        }
        switch(MinusArea)
        {
            case Top:
                if((new Rectangle(...)).Contains(point))
                    return true;
            ...
        }
        return false;
    }

Then puzzle is

public class Puzzle
{
    public List<Piece> Pieces {get; set;}

    public void Draw(Graphics graphics)
    {
        // draw all pictures with respect to z order
    }

    public Piece HitTest(Point point)
    {
        ... // hittest all pieces, return highest z-order or null
    }
}

It is not a complete solution, but should give you idea.

Basically:

  • In mouse event you call Figure.HitTest() to get figure to start moving (that's what you need).
  • You draw everything into owner drawn control by calling Figure.Draw().
  • Obviously, drag-n-drop operations are calling Invalidate().
  • You may have special flag to indicate figure being dragged and draw it differently (with shadows, a bit offsetted to simulate it's pulled over other pieces, etc).
  • Each figure is represented as rectangle and PlusArea or MinusArea (don't know how to call them better, it's this extra or missing area of piece connectors), this is simplification, you can improve it.

In general, keep a list of all your puzzle piece controls, sorted top down. When you get a mouse down event on one piece, check the transparency at that point, if it it not transparent handle the event on that piece. If it is transparent forward the event to the next piece down in your list (directly calling the event handler is probably the easiest way). Keep doing this until you either find a non transparent point, or run out of pieces.

UPDATE Here is a link to a project showing an example of how to do this in pure GDI. https://drive.google.com/file/d/0B42fIyGTLNv3WlJwNGVRN2txTGs/edit?usp=sharing

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top