Animowane przejście loga w prostej grze

0

Witam. Jestem tu nowy. Uczę się pisząc jakiś program. Czasami jestem w stanie zrobić coś sam, czasami szukam w internecie. Pierwszy raz mi się zdarzyło, że sam nie potrafię sobie poradzić. Jaki mam problem? Zabieram się pewnie za coś co przekracza moje umiejętności, ale nie szkodzi mi spróbować. A więc próbuje napisać małą grę. O ile wszystko mam zaplanowane i już wcześniej testowane różne mechanizmy to teraz chciałbym zacząć pisać wszystko jakby od początku już dodając interfejs. Tak na prawdę na samym początku mam pewien problem. Oczywiście to co chce zrobić nie jest potrzebne, obejdzie się bez tego, ale chciałbym żeby było efektywniej. Ma to wyglądać tak: po włączeniu programu ma być czarny ekran przez 1, 2 sekundy. Później od lewej strony ma wjeżdżać tło. Będąc na środku ma się zatrzymać na chwilę. W tym momencie ma się pojawić logo. Przez 2 sekundy ma tak być, a później wszystko w drugą stronę. Logo ma zniknąć, a tło przejść w prawą stronę. Po zniknięciu ma być czarny ekran przez chwilę po czym ma pojawić się menu.

Próbowałem to zrobić z użyciem zegara, który jest uruchamiany w zdarzeniu onCreate całego programu. Próbowałem też użyć delay(2000). O ile tło wjeżdża prawidłowo i zatrzymuje się to nie wiem co zrobić dalej. W momencie wjechania na odpowiednie miejsce ustawiam clock.enabled na false. Później chciałbym odczekać te dwie sekundy za pomocą delay. Ale wtedy to wgl nie chce się uruchomić. poza tym później, żeby tło wyjechało trzeba uruchomić zegar ponownie, a z tym już mam problem.
Możecie mnie jakoś naproawdzić?
Wielkie dzięki

1

Wszystko możesz wykonać za pomocą jednego Timera - wystarczy chwilę pomyśleć;

Jak rozumiem najpierw ma być czarne tło, następnie logo wjeżdża, zatrzymuje się na dwie sekundy, następnie logo wyjeżdża i znów zatrzymuje się na dwie sekundy; Zrób więc Timer, który będzie miał mały interwał - będzie on określał ilość klatek na sekundę, np. Timer.Interval = 40 da 25 klatek na sekundę, czyli powinno być płynnie; Niech ten Timer korzysta z licznika, czyli zmiennej liczbowej, którą będzie inkrementować w każdym wywołaniu zdarzenia OnTimer; Współrzędne grafiki loga albo trzymaj w jakiejś tablicy, albo obliczaj na bieżąco, na podstawie wartości licznika;

Teraz w zdarzeniu OnTimer przed inkrementacją licznika sprawdzaj w instrukcji Case wartość licznika i wykonuj odpowiednią operację - albo czekaj, albo przesuwaj grafikę w określoną stronę:

case intCounter of // intCounter to licznik
  0   .. 49:  { wstępne czekanie dwie sekundy na wjazd loga } ;
  50  .. 74:  { przesuwanie grafiki loga z lewej na środek  } ;
  75  .. 124: { oczekiwanie dwie sekundy z logiem na środku } ;
  125 .. 149: { przesuwanie grafiki loga ze środka na prawą } ;
  150 .. 199: { końcowe czekanie dwie sekundy na menu       } ;
  200:        { wyłączenie zegara                           } ;
end;

Inc(intCounter);

Najtrudniejsze będzie przesuwanie loga, ale to zwykła arytmetyka na poziomie podstawówki, więc na pewno sobie poradzisz.

1

Aktualny stan animacji możesz zapisać do osobnego pola klasy lub np do clock.Tag wyłączaj zegar dopiero kiedy w całości skończysz animacje, w pozostałych przypadkach jedynie przestawiaj Interval.

0

Dzięki wielkie, dopiero teraz tak całkiem zrozumiałem działanie zegara. Ja próbowałem to zrobić tak jakby wszystko za jednym "przejściem". Ale zamiast Case użyłem If. Czy to jest jakiś duży błąd czy też może zostać?

1

Ja próbowałem to zrobić tak jakby wszystko za jednym "przejściem".

W ten sposób to nie zadziała jak należy, a nawet jeżeli udałoby Ci się tak zrobić, to Timer byłby zbędny;

Timer odlicza mniej więcej określoną ilość czasu (ustaloną we właściwości Interval) i każdorazowo po upływie zadanej ilości czasu, wykonuje kod ze zdarzenia OnTimer; Albo używaj Timera do odliczania "klatek", albo zmieniaj mu Interval; Odliczanie ilości klatek można tak łatwo wykonać, jak pokazałem w swoim poprzednim poście; Zmiennym interwałem można zastąpić oczekiwanie tych kilku sekund na wjazd/wyjazd loga i na zatrzymanie loga na środku ekranu; Zrób jak chcesz, byle by działało i nie blokowało interfejsu;

Ale zamiast Case użyłem If. Czy to jest jakiś duży błąd czy też może zostać?

Można, tyle że w zależności od wybranego sposobu może z nich wyjść cała drabinka, którą skrócić można właście instrukcją wyboru (Case);

Jak znajdę chwilę to pokażę implementację sposobu, który opisałem wczesniej.

0

Działać działa tak jak chciałem, więc jest wszystko ok. Rozwiązanie było proste, a ja próbowałem na około. Dziękuję raz jeszcze.

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls;

type

  { TGra }

  TGra = class(TForm)
    ImageLogo: TImage;
    ImageTloStart: TImage;
    PanelWstep: TPanel;
    TimerWejscie: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure TimerWejscieTimer(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    SzerokoscTla: Integer;
    WysokoscTla: Integer;
    SzerokoscLoga: Integer;
    WysokoscLoga: Integer;
    LicznikTimerWejscie: Integer;
  end;

var
  Gra: TGra;

implementation

{$R *.lfm}

function GetImageSize (const Path: string; Height: boolean = False): integer;
var Image: TImage;
begin
  if ExtractFileExt(Path) <> '' then
    try
      Image := TImage.Create(nil);
      Image.Picture.LoadFromFile(Path);
      if not Height then
        Result := Image.Picture.Width
      else
        Result := Image.Picture.Height;
    finally
      Image.Free;
    end;
end;

procedure WczytajRozmiarTla;
begin
  Gra.SzerokoscTla := GetImageSize('backgrounds/background1full.png');
  Gra.WysokoscTla := GetImageSize('backgrounds/background1full.png', True);
end;

procedure WczytajRozmiarLoga;
begin
  Gra.SzerokoscLoga := GetImageSize('logos/mainlogo.png');
  Gra.WysokoscLoga := GetImageSize('logos/mainlogo.png', True);
end;

{ TGra }

procedure TGra.FormCreate(Sender: TObject);
begin
  WczytajRozmiarTla;
  WczytajRozmiarLoga;

  Gra.Left := 0;
  Gra.Top := 0;
  Gra.Width := screen.Width;
  Gra.Height := screen.Height;

  PanelWstep.Left := 0;
  PanelWstep.Top := 0;
  PanelWstep.Width := screen.Width;
  PanelWstep.Height := screen.Height;
    ImageTloStart.Width := SzerokoscTla;
    ImageTloStart.Height := WysokoscTla;
    ImageTloStart.Left := 0 - SzerokoscTla;
    ImageTloStart.Top := Round((screen.Height - WysokoscTla) / 2);
    ImageTloStart.Picture.LoadFromFile('backgrounds/background1full.png');

    ImageLogo.Width := SzerokoscLoga;
    ImageLogo.Height := WysokoscLoga;
    ImageLogo.Left := Round((screen.Width - SzerokoscLoga) / 2);
    ImageLogo.Top := Round((screen.Height - WysokoscLoga) / 2);
    ImageLogo.Picture.LoadFromFile('logos/mainlogo.png');

    TimerWejscie.Enabled := True;
    LicznikTimerWejscie := 0;
end;

procedure TGra.TimerWejscieTimer(Sender: TObject);
begin
  Inc(LicznikTimerWejscie); 
  if LicznikTimerWejscie = 453 then
    begin
      TimerWejscie.Enabled := False;
      PanelWstep.Visible := False;
      PanelMenuGlowne.Visible := True;
    end
  else
    if LicznikTimerWejscie < 101 then
      ImageTloStart.Visible := True
    else
      if LicznikTimerWejscie < 177 then
        begin
          ImageTloStart.Left := ImageTloStart.Left + Round(((Round((screen.Width - SzerokoscTla) / 2)) + SzerokoscTla) / 76);
          if LicznikTimerWejscie = 176 then
            ImageLogo.Visible := True;
        end
      else
        if LicznikTimerWejscie < 277 then
          begin
            TimerWejscie.Interval := 20;
            if LicznikTimerWejscie = 276 then
              ImageLogo.Visible := False;
          end
        else
          if LicznikTimerWejscie < 353 then
            begin
              TimerWejscie.Interval := 5;
              ImageTloStart.Left := ImageTloStart.Left + Round(((Round((screen.Width - SzerokoscTla) / 2)) + SzerokoscTla) / 76);
            end
          else
            if LicznikTimerWejscie < 453 then
              ImageTloStart.Visible := False;
end;

end.

1

No mogę dołączyć kod, ale przede wszystkim nazwy są takie żebym ja wiedział co jest co, chociaż pewnie każdy zrozumie to jak przeanalizuje.

Tym się nie przejmuj - nazewnictwo jest mniej ważne niż choćby formatowanie kodu;

Bardziej obawiam się, że nie jest to napisane tak jak być powinno.

No to czemu nie pytasz dalej? Trzeba było od razu pokazać kod i poprosić o sprawdzenie;


Przede wszystkim zamień tę drabinkę **If**ów na instrukcję wyboru (czyli Case) - zobaczysz jak kod się skróci i jak zwiększy się jego czytelność; Możesz przecież korzystać z zakresów - podałem Ci wcześniej przykład takiej instrukcji;

Nie wiem też po co wyłączasz i włączasz Timer - wszystko może przebiegać na ciągłej pracy zegara; Dodatkowo, w tych etapach, w których ma być stały czarny ekran oraz kiedy logo ma stać bez ruchu na środku ekranu, nie musisz liczyć klatek; Wystarczy zmienić Interval od razu na liczbę np. 2000 (czyli dwie sekundy), dzięki czemu program będzie czekał faktyczne dwie sekundy, a zmienną LicznikTimerWejście inkrementujesz tylko raz; Czyli podsumowując - możesz inkrementować licznik tylko podczas jednej z dwóch animacji ruchu loga;

To w sumie tyle - dobrze by było, gdybyś dodał do załączników posta źródła Twojego projektu, aby można było przetestować kod u siebie; Łatwiej by było pokazywać co można zmienić i wymieniać się plikami.

0

W sumie nie chcę żeby ktoś jakoś za bardzo ingerował w ten kod. Chce to zrobić sam a jedynie się pytać o coś tylko jeśli sobie nie potrafię poradzić.

0

Nie musisz podawać właściwego kodu Twojego projektu - chodzi o jakikolwiek projekt, który implementuje funkcję animowanego loga; Może to być np. aplikacja testowa, która prócz animacji loga nic innego nie robi;

Łatwiej doradzić coś, jeśli ma się kod do dyspozycji, można go skompilować i przetestować; Tobie też będzie lepiej zobaczyć poprawiony kod i poczytać do niego wskazówki - szybciej zapamiętasz dobre techniki pisania kodu; Ale zmuszać Cię nie będę;

Jeżeli więc dalej masz jakiekolwiek problemy z tą animacją to pytaj.

0

Nie nie. Już problemów żadnych nie mam. Wszystko działa tak jak miało być. A instrukcję if ostatecznie zamieniłem na case. Jest czytelniej, zajmuje mniej miejsca.

A jeśli będę miał jeszcze jakiś problem to następnym razem będę wstawiał załączniki.

2
dani17 napisał(a):
  Gra.Left := 0;
  Gra.Top := 0;
  Gra.Width := screen.Width;
  Gra.Height := screen.Height;

Można zamienić na:

  Gra.BoundsRect:=Bounds(0,0,screen.Width.screen.Height);

1 użytkowników online, w tym zalogowanych: 0, gości: 1