Diskuze: Animovaná změna pozice - WinForms

C# .NET .NET (C# a Visual Basic) Animovaná změna pozice - WinForms American English version English version

Avatar
Ondrca
Redaktor
Avatar
Ondrca:

Čau lidi, neznáte nějaký způsob jak animovaně (aby to tam neskočilo) změnit pozici třeba nějakého labelu atd... (WinForms)?
Přesněji mám PictureBox na který vykresluji fillRectangle() a vždy mu změním souřadnice, na pozici kam kliknu myší, když chci aby mi tam skočil, tak tam skočí pěkně, ale já bych chtěl aby tam pěkně animovaně dojel.
Zde je kód:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Test
{
    public partial class Level1 : Form
    {
        public int StartX = 0;
        public int StartY = 0;
        public int ActualX = 0;
        public int ActualY = 0;
        public Level1()
        {
            InitializeComponent();
        }
        private void Vykresli(Graphics g,int X,int Y)
        {
            g.FillRectangle(Brushes.DarkBlue, X, Y, 50, 50);
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Vykresli(e.Graphics,ActualX,ActualY);
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            MouseEventArgs me = (MouseEventArgs)e;
            StartX = ActualX;
            StartY = ActualY;
            ActualX = me.Location.X;
            ActualY = me.Location.Y;
            plocha.Invalidate();
            Vykresli(plocha.CreateGraphics(), StartX, StartY);
        }

Pro to animování jsem zkoušel přidat toto:

private void moveTimer_Tick(object sender, EventArgs e)
        {
            if (StartX < ActualX)
            {
                StartX += 10;
                if (StartY < ActualY)
                {
                    StartY += 10;
                }
                if (StartY > ActualY)
                {
                    StartY -= 10;
                }
                plocha.Invalidate();
                Vykresli(plocha.CreateGraphics(), StartX, StartY);
            }
            if (StartX < ActualX)
            {
                StartX -= 10;
                if (StartY < ActualY)
                {
                    StartY += 10;
                }
                if (StartY > ActualY)
                {
                    StartY -= 10;
                }
                plocha.Invalidate();
                Vykresli(plocha.CreateGraphics(), StartX, StartY);
            }
        }

Taky bez úspěchu.
Předem díky a přeji vám všem šťastný nový rok 2015 :)

Odpovědět 31.12.2014 14:08
Zase jsem o něco chytřejší
Avatar
Lukáš Křehula
Redaktor
Avatar
Odpovídá na Ondrca
Lukáš Křehula:

Mrkni na tohle http://www.itnetwork.cz/…yb-po-primce
Můžeš si na tom zvolit i rychlost pixelů za sekundu, pak už se funkce na tohle dělá jedna báseň :)
Jestli bys nevěděl jak na to tak ti klidně pomůžu.

Editováno 31.12.2014 14:56
 
Nahoru Odpovědět 31.12.2014 14:56
Avatar
rwn
Člen
Avatar
Odpovídá na Ondrca
rwn:

Ahoj, tak jsem si to nějak prošel co v tom děláš a máš tam několi nedostatků. :)

  1. Používáš zde moveTimer_Tick, což je asi událost časovače, který ale nikde nemáš, takže se ti obsah moveTimer_Tick nikdy neprovede.
  2. Používání pictureBox1_Paint funguje tak, že se obsah této události vykreslí vždy při jaké koliv změně na tvém pictureboxu1, tím, že si ActualX a ActualY nastavuješ po kliknutí, tak ti tahle událost
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    Vykresli(e.Graphics,ActualX,ActualY);
}

bude kreslit vždy jen výsledek a nikdy přechod.

  1. Není zrovna vhodné si pojmenovat pictureBox1 na plochu a událostem na této ploše nechat starý název pictureBox1 _neco, je to docela matoucí. :)
  2. Pokud si chceš provést aktuální vykreslení, tak není potřeba volat metodu na vykreslení, ale pouze plocha.Refresh(); která provede zavolání té tvojí události pictureBox1_Paint (v tom případě ti stačí si přenastavovat parametry tohoto vykreslení jen) :)

Tvůj kód by teda mohl vypadat nějak takto, snažil jsem se toho moc neměnit, ikdyž některé ty věci by se daly udělat i mnohem efektivněji :)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

namespace Test
{
    public partial class Level1 : Form
    {
        public int StartX = 0;
        public int StartY = 0;

        public int ActualX = 0;
        public int ActualY = 0;

        public int FinishX = 0;
        public int FinishY = 0;

        public int speed = 5;

        public Level1()
        {
            InitializeComponent();
        }

        private void Vykresli(Graphics g, int X, int Y)
        {
            g.FillRectangle(Brushes.DarkBlue, X, Y, 50, 50);
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Vykresli(e.Graphics, ActualX, ActualY);
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            MouseEventArgs me = (MouseEventArgs)e;
            StartX = ActualX;
            StartY = ActualY;

            FinishX = me.Location.X;
            FinishY = me.Location.Y;

            plocha.Invalidate();

            System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
            myTimer.Tick += new EventHandler(moveTimer_Tick);
            myTimer.Interval = 50;
            myTimer.Start();
        }

        private void moveTimer_Tick(object sender, EventArgs e)
        {
            if (FinishX != ActualX)
            {
                if (FinishX < ActualX)
                {
                    ActualX -= speed;
                }

                if (FinishX > ActualX)
                {
                    ActualX += speed;
                }
            }

            if (FinishY != ActualY)
            {
                if (FinishY < ActualY)
                {
                    ActualY -= speed;
                }

                if (FinishY > ActualY)
                {
                    ActualY += speed;
                }
            }

            plocha.Refresh();
        }
    }
}

Zavedl jsem tam nové proměnné. Při vytvážení pohybu je dobré mít totiž začátek, konec a aktuální hodnotu. :) Pak jsem tam přidal ještě pomyslný speed, která určuje o kolik se v daném čase má kostka posunout. Co se týče události moveTimer_Tick, tak jsem jí docela předělal, protože mě nedávala moc smysl jak jsi jí napsal, v jedné podmínce X zvětšíš a ve druhé jí hned zmenšíš, asis jí moc neodladil. :) Jinak v této události si na konci volám zmíněný plocha.Refresh;, který jen provede pictureBox1_Paint už s novýma souřadnicema. Takhle jak jsem ti to upravil je to už funkční, samozřejmě je tam nedostatek v tom, že se ti posouvá vždy šikmo XY, a když jeden parametr (X nebo Y) dosáhne cíle, tak se ti posouvá už jen vodorovně nebo svisle, ale tak by se dalo jen lépe přepočítávat. :)
Jinak ještě co se týče animací a obecného vykreslování, tak WF není zrovna na to nejlepší. Dělal jsem docela dost programů na vykreslování a animace a WF má docela problém s vykreslováním - co se náročnosti týče, takže se ti může stát, že se ti to začne najednou sekat. :) Obecně pro vykreslování a tyto animace doporučuju WPF, je tam sice trochu jiný způsob, jakým vykreslování programovat, ale pokud se ti to bude někdy sekat, tak bys měl přejít na WPF. To je tak vše co bych k tomu měl. :)

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
Nahoru Odpovědět 8.1.2015 19:41
Co můžeš naprogramovat dnes, neodkládej na zítřek.
Avatar
Ondrca
Redaktor
Avatar
Odpovídá na rwn
Ondrca:

Díky :) - na WPF jsem koukal a radši chci přejít rovnou na MonoGame

Nahoru Odpovědět 8.1.2015 19:46
Zase jsem o něco chytřejší
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.