Zdravím, řešim jeden problem.
Chtěl jsem udělat jednu takovou blbost, prostě kulička (Shape) která by reagovala na zmáčknutí šipek, pohybovala by se, skákala by po takovych plošinkách (pole shapů). Udělal jsem to přes timer (interval 10, vždy zjišťoval GetKeyState(vk_left), vk_right a vk_up ...), vše jsem měl, běhalo to v pohodě, fungovalo jak mělo.
Ale logicky, taková "hra" je o ničem, tak jsem se rozhodl, že přidám na každou plošinku (je jich 11+podlaha) kromě podlahy jednu kuličku(pole shapů) která se bude pohybovat po te plošince od jedné hrany k druhé, při dotyku s mou bude konec hry. Když jsem měl hotov pohyb kuliček (přes další timer, interval 10, pouze posunoval kuličky) a chtěl jsem se pustit do kontroly kolize, otestoval jsem to a co nevidím, hra se při pohybu mou kuličkou dost zpomaluje.
Napadlo mne, že ten prvni timer to asi hodně vytěžuje, chtělo by to pořešit jinak. Chtěl jsem to tedy dát na OnKeyDown formuláře.
Narazil jsem ale na problem, kvuli kteremu jsem to již na začátku dal na timer.
Při zmáčknutí a držení klávesy se sice kulička pohybuje, ale ne ideálně. Vite jak třeba prijíždíte textem a držíte klávesu? Kurzor se posune o jednu pozici, pak má chvíli pauzu a až po té pauze se začne svižně pohybovat dál. Stejně se hýbe i má kulička. Jak se zbavím té pomlky?
Fórum › Delphi
Plynulý posun
Nevim zbavovat se kvuli tomu pomlky mi přijde absurdní. Vem to takhle. Asi ti to zpomaluje testování kolizí při tvém
pohybu, nemáš to napsaný nějak neoptimálně tu rutinu pro kolize?
I když to 2 Timery zpomalovat nebudou stačí ti stejnak jeden. Z
jednoho OnEvent Timeru
můžeš řešit pohyb všech objektů. Si tam dej případně počítadlo - proměnnou kolik uběhlo času
(tak můžeš pohyb svý kuličky občas vynechat) a
podprocedury, kterýma vyřešíš pohyb všech objektů ve hře.
Taky proč než shape to nekreslíš do Canvasu? Až přejdeš na něj tak poznáš že je lepší na hry :)
yop dam to teda do jednoho timeru, je to fakt, nevim proč sem to tak nedal rovnou.... no do canvasu jsem to nekreslil proto, že to pořadně neumim (jsem začátečník), tak pořádně nevim jak na to, ale projdu nějaky kurzy a přijdu canvasu na kloub ;). diky
tak sem to udělal teda tak, že jsem si na vše vytvořil misto shapu image a do nich jsem to přes canvas nakreslil (nevim jak bych přesně dělal ten pohyb, kdybych to canvasem kreslil přimo na form...). Pohybuje se to o něco plynulejc, ale furt to neni ono, ale to už bude problem v procedurach kontrolujicich, jestli je kulička na plošince (pokud neni, má padat dolu).
Dá se ta kontrola udělat nějak jinak? Mám tam poc-1 plošinek (plocinka poc je podlaha), nepritel jsou ty dalsi kulicky, ikdyz necham na timeru jenom tohle, furt se me zda ze to jede celkem pomalu, cca od intervalu 40 dolu se nezrychlují, to uz rychleji nedokaze? max je maximalni hodnota kam muze kazda kulicka jet (plosina[k].left+plosina[k].width) a min=plosina[k].left:
for k:=0 to (poc-1) do
begin
if nepritel[k].left=min[k] then pos[k]:=1
else if nepritel[k].left=max[k] then pos[k]:=-1;
nepritel[k].Left:=nepritel[k].Left+pos[k];
end;
Nejvic to asi zatežuje to, když kontroluju jestli ma kulička je na nějake plošince. Projiždim všechny plošinky, zjistim jestli plosinka[k].top=(kulicka.top+kulicka.height) a pak ještě jestli kulicka.left>plosinka[k].left and kulicka.left+kulicka.width<plosinka[k].left+plosinka[k].width a podle toho zjistim jestli je na nektere plošince, jestli neni, tak pada dolu. Nedalo by se nějak lip zjistit, jeslti je na nějake plošince?
Tak třeba zpomaluje práce se standardníma objektama Delphi, používej jak já pole svých vlstních nadefinovaných objektů
nebo Recordů(ještě rychlejší), bude to rychlejší v přístupu, ale u tebe bych řekl spíš variantu že za to můžou Windows - oni taky nejsou s časovačem u malejch hodnot slavný. Můžeš to zkusit dít do procedury Application.OnIdle - ta se vykonává když nemá program do čeho píchnout. Normálně tomu přiřaď svou proceduru.
Na takovejch víc jak 100 plošinek můžeš použít algoritmus stromů, ale tolik jich snad nemáš..
mam dotaz. mam tenhle kod:
TKula = class
Canvas:TCanvas;
FLeft:integer;
FHeight:integer;
FWidth:integer;
FTop:integer;
function RLeft:integer;
function RHeight:integer;
function RWidth:integer;
function RTop:integer;
procedure WLeft(i:integer);
procedure WHeight(i:integer);
procedure WWidth(i:integer);
procedure WTop(i:integer);
property Left:integer read RLeft write WLeft;
property Height:integer read RHeight write WHeight;
property Width:integer read RWidth write WWidth;
property Top:integer read RTop write WTop;
end;
var
Form1: TForm1;
Kula: Tkula;
implementation
{$R *.dfm}
function TKula.RLeft;
begin
RLeft:=FLeft;
end;
procedure TKula.WLeft(i:integer);
begin
FLeft:=i;
end;
function TKula.RTop;
begin
RTop:=FTop;
end;
procedure TKula.WTop(i:integer);
begin
FTop:=i;
end;
function TKula.RHeight;
begin
RHeight:=FHeight;
end;
procedure TKula.WHeight(i:integer);
begin
FHeight:=i;
end;
function TKula.RWidth;
begin
RWidth:=FWidth;
end;
procedure TKula.WWidth(i:integer);
begin
FWidth:=i;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
kula:=TKula.Create;
kula.Canvas:=Tcanvas.Create;
kula.Left:=3;
kula.Top:=3;
kula.Height:=3;
kula.Width:=3;
kula.Color:=clblack;
kula.Canvas.Brush.Color:=clblack;
kula.Canvas.Ellipse((0,0,kula.Width,kula.height);
a vypisuje to chybu "canvas does not allow drawing"
Co dělám špatně? Vlastnosti left, top, width a height - jsou potřeba ještě nějak specialně propojovat s aplikací (resp. s formulářem), aby se brali jako klasicke vlastnosti (odsazeni zleva, zvrchu, šířka, výška)?
Jo nějak takhle jsem to myslel, učíš se rychle, s těma objektama, akorát ti tam chybí třeba Hitpointy, vykonávaná činnost, rychlost a další.
To by sis ale moc nepomohl dávat každé kuličce vlastní canvas(taky pomalý a nepraktický), stačí ti vykreslovat do jednoho Canvasu, nejlépe přes nějaké kompatibilní memory Device Context, Svému jednomu canvasu vně (jako globální proměnná) pak přiřadíš handle (jeho jeden z členů) z týhle funkce (CreateCompatibleDC) tak to dělám já. A vytvoříš si pro něj kompatibilní bitmapu do které se bude vykreslovat o daném rozlišení tvého okna. Nakonec budeš překreslovat každý frame v události OnPaint překopíruješ svůj 1 globální Canvas pomocí CopyRect.
Yop ty property byly jenom orientačně, abych věděl zda něco nedělam špatně :).
Dále jsem to mírně nepochytil :).
Takže, vytvořím si jeden globalni canvas.
Vykres:TCanvas
a ve FormCreate
Vykres:=Tcanvas.Create;
A pomocí f-ce CreateCompatibleDC přiřadim handle?
CreateCompatibleDC(Vykres.handle);
Asi ne že? Jelikož u tohoto kroku mi to vždy spadne a vyhodi hlašku "Canvas does not allow drawing"
nemyslíš si že se svou otevřenou hlavou - že to z tohohle popisu snad zvládneš že ne:
správně sis vytvořil 1 třídu canvasu(resp. inicializoval)
teď vezmi jeden nějakej pomocnej handle, a do něho přiřaď kompatibilní DC
který inicializuješ handlem toho svého globálního canvasu. Chce to jen tenhle jeden
argument.
Pak je důležité vytvořit Kompatibilní Bitmapu zas přes handle svýho canvasu s danym rozlišenim xy.
Pomocí selectobject navol na ten pomocnej handle co máš (pořád ten samej) odkaz na vytvořenou bitmapu
Do královského glob. canvasu jeho člena handle přiřaď opět pomocný handle.
Toť vše u startu. Překopírování v události OnPaint je hračka..
předem odmítám nařčení z masochismu :D
To illioner :
fííha, asi sem moc dutej :)...
správně sis vytvořil 1 třídu canvasu(resp. inicializoval) - to teda mam
teď vezmi jeden nějakej pomocnej handle, a do něho přiřaď kompatibilní DC který inicializuješ handlem toho svého globálního canvasu - to jsem pochopil takto (han je pomocný handle):
han:=CreateCompatibleDC(vykres.Handle);
Ovšem u tohoto to vždy skončí s chybou "Canvas does not allow drawing"
Pak teda jestli jsem to dobře pochopil dál:
Pak je důležité vytvořit Kompatibilní Bitmapu zas přes handle svýho canvasu s danym rozlišenim xy. - žeby tak?
Vykres.Brush.Bitmap:=TBitmap.Create;
Vykres.Brush.Bitmap.Width:=form1.Width;
Vykres.Brush.Bitmap.Height:=form1.Height;
Vykres.Brush.Bitmap.Handle:=CreateCompatibleDC(vykres.Handle);
Pomocí selectobject navol na ten pomocnej handle co máš (pořád ten samej) odkaz na vytvořenou bitmapu -
SelectObject(han,Vykres.Brush.Bitmap.Handle);
Do královského glob. canvasu jeho člena handle přiřaď opět pomocný handle. -
Vykres.Handle:=han;
Tak, co dělm špatně? Ještě se omlouvám jestli obtěžuju mou nechápavostí, ale jsem začátečník :)
tenhle radek tam mas ale navic
Vykres.Brush.Bitmap.Handle:=CreateCompatibleDC(vykres.Handle);
jinak zcela presne, ja s tim delam uz kolikatej program, nemuze ti to zlobit a padat, to uz spis hledejme chybu jinde.
Dej na zacatek sveho timerovaneho kresleni Canvas.Lock a na konec Unlock.
a nezapomen postovat .exe dema :D
no já to zkoušim zatim nanečisto do prazdne aplikace, než to vpustim do te hry, ale i tak, nevim v čem bych tam jinak měl chybu.
TKula = class
FLeft:integer;
FHeight:integer;
FWidth:integer;
FTop:integer;
function RLeft:integer;
function RHeight:integer;
function RWidth:integer;
function RTop:integer;
procedure WLeft(i:integer);
procedure WHeight(i:integer);
procedure WWidth(i:integer);
procedure WTop(i:integer);
property Left:integer read RLeft write WLeft;
property Height:integer read RHeight write WHeight;
property Width:integer read RWidth write WWidth;
property Top:integer read RTop write WTop;
constructor Create;
end;
var
Form1: TForm1;
Kula: Tkula;
Vykres: TCanvas;
BMap:TBitmap;
han:HDC;
implementation
{$R *.dfm}
constructor TKula.Create;
begin
left:=3;
top:=3;
width:=100;
height:=100;
end;
function TKula.RLeft;
begin
RLeft:=FLeft;
end;
procedure TKula.WLeft(i:integer);
begin
FLeft:=i;
end;
function TKula.RTop;
begin
RTop:=FTop;
end;
procedure TKula.WTop(i:integer);
begin
FTop:=i;
end;
function TKula.RHeight;
begin
RHeight:=FHeight;
end;
procedure TKula.WHeight(i:integer);
begin
FHeight:=i;
end;
function TKula.RWidth;
begin
RWidth:=FWidth;
end;
procedure TKula.WWidth(i:integer);
begin
FWidth:=i;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
kula:=TKula.Create;
Vykres:=TCanvas.Create;
han:=CreateCompatibleDC(vykres.Handle);
Vykres.Brush.Bitmap:=TBitmap.Create;
Vykres.Brush.Bitmap.Width:=form1.Width;
Vykres.Brush.Bitmap.Height:=form1.Height;
SelectObject(han,Vykres.Brush.Bitmap.Handle);
Vykres.Handle:=han;
end;
Při tomto kodu to stále háže "canvas does not allow drawing".. zkoušel jsem před to dat vykres.lock i vykres.trylock, ale nic.
A je vůbec ten Handle platný? Neměl by sis ho napřed sám získat?:
vykres:=TCanvas.Create;
vykres.Handle := GetDC(0);
han:=CreateCompatibleDC(vykres.Handle);
A ještě poznámku, nevím jak v Delphi, ale jinde je potřeba k CompatibleDC ještě tvořit CompatibleBitmap, takže něco takového:
Vykres.Brush.Bitmap.Handle:=CreateCompatibleBitmap(vykres.Handle, form1.Width, form1.Height);
takhle je to spravne:
procedure TForm1.FormCreate(Sender: TObject);
begin
kula:=TKula.Create;
Vykres:=TCanvas.Create;
han:=CreateCompatibleDC(canvas.Handle);
Vykres.Brush.Bitmap:=TBitmap.Create;
Vykres.Brush.Bitmap.Width:=form1.Width;
Vykres.Brush.Bitmap.Height:=form1.Height;
SelectObject(han,Vykres.Brush.Bitmap.Handle);
Vykres.Handle:=han;
end;
po přidání
vykres.Handle := GetDC(0);
již chybu nevypisuje.
Ovšem když zkusim jenom třeba na tlačitko dat
vykres.Brush.color:=clblack;
vykres.Ellipse(0,0,30,30);
tak se nic neděje.
1) proč se nic neděje?
2) Sice je pěkny že sem si to za vaši pomoci dokazal takhle vytvořit, ale jak pak dál? :) To při každem pohybu budu překreslovat cely canvas a vykreslovat kuličky na novych pozicich? Nebude to pomaly?
add 1) Ono se děje, ale děje se to v DC, který je v paměti. Při vykreslení (OnPaint) musíš přes funkci BitBlt překopírovat obsah paměťového DC do okénkového DC.
add 2) V podstatě ano. Při každém překreslení musíš překopírovat celou viditelnou výseč DC (BitBlt). Při každém pohybu to celé překreslovat nemusíš, stačí ti změnit oblast (region), kde se něco změní. Pro začátek ovšem můžeš kreslit všechno znovu a i tak to v tomto případě nebude pomalé.
Velice se omlouvám za dlouhou pauzu, ale mě jsem problémy s internetem a taky hodně práce... jestli je ještě někdo ochotný, rád bych pokračoval v projektu :).
na FormCreate tedy mam
kula:=TKula.Create;
Vykres:=TCanvas.Create;
han:=CreateCompatibleDC(canvas.Handle);
Vykres.Brush.Bitmap:=TBitmap.Create;
Vykres.Brush.Bitmap.Width:=form1.Width;
Vykres.Brush.Bitmap.Height:=form1.Height;
SelectObject(han,Vykres.Brush.Bitmap.Handle);
Vykres.Handle:=han;
vykres.Handle := GetDC(0);
dále na OnPaint mam
bitblt(form1.Canvas.Handle,0,0,40,40,vykres.Handle,0,0,0);
(snad jsem to pochopil dobře)
a na stisku tlačítka
Vykres.Brush.Color:=clyellow;
vykres.Ellipse(0,0,40,40);
Spustim program a na formuláři se mi objeví černý čtverec o souřadnicích 0,0,40,40.
Stisknu tlačítko a v levém horním rožku se mi objeví černý čtverec opět 40x40 a v něm žlutá kružnice (r=20).
Mám par otázek.
1) na co je mi tu procedura OnPaint, když už mi to vykresluje i tak?
2) jak to donutim, aby mi to vykreslovalo tu kružnici v oblasti, kde je formulář (resp. jak zjistim souřadnice formuláře na displeji)?
3) když program zminimalizuju a zmaximalizuju, kružnice zmizi. Tohle ošetřím jak?
Předem moc diky za vaši trpělivost se mnou :)
Několik poznámek ... v té první části je určitě navíc řádek:
vykres.Handle := GetDC(0);
už sis Handle naplnil vytvořeným CompatibleDC.
Další věc ... předtím jsem tomu něvěnoval pozornost ... Canvas.Brush.Bitmap bude bitmapa pro štětec na vyplňování. Jak máš založenou proměnnou TCanvas, tak si založ i TBitmap a Canvas.Brush.Bitmap vynechej (nahraď).
POslední parametr při BitBlt je typ bltování :smile1: . 0 je určitě špatně resp. musíš použít konstantu SRCCOPY:
bitblt(form1.Canvas.Handle,0,0,40,40,vykres.Handle,0,0,SRCCOPY);
A k tvým otázkám:
add 1) v OnPaint se vykresluje, můžeš vykreslit i na základě jiné události, ale pak ti to např. při min/max (add 3) může zmizet (i když tady ti to mizelo z jiných důvodů)
add 2) GetClientRect, GetWindowRect ...
add 3) když uděláš ty úpravy, jak jsem napsal výše, tak nebude co ošetřovat. Kružnici nakreslíš do paměti a v OnPaint se ti zase vykreslí. Možná bude potřeba na konec stisku tlačítka přidat Repaint.
ok, pak zkusim, ale bez řádku
vykres.Handle := GetDC(0);
mi to při spouštění vyhazuje chybu o které jsem se zmiňoval již výše.
Za ten parametr v BitBlt se omlouvám, neměl jsem tušení co to je :).
A ještě, jak přesně mysliš nahrazení té bitmapy tou vytvořenou?
A ještě mě napadlo (doufám že už jsem se na to neptal), co kdybych to vše vykresloval přímo do canvasu toho formuláře a v něm to překresloval? Ale je to jenom takovej nápad, chápu, pokud je to nějaky špatny. Upřimně bych raději pokračoval v tomhle, abych se toho co nejvice přiučil, pokud se mnou budete mit trpělivost :)
Všechno souvisí se vším :smile1: .
Když to uděláš přímo, tak se nevyhneš jedné nepříjemné věci, a to takové, že ti to bude problikávat. Proto se to řeší přes double buffering (to je to, o co se tu snažíš), kdy si všechno nakreslíš do paměti a na obrazovku to jde v jedné várce a bez blikání.
BitBlt je mocná funkce. Ona toho umí spoustu a ten poslední parametr je vlastně ten nejdůležitější. Doporučuji ti udělat si s ní potom pár pokusů, abys viděl, co všechno dokáže. Až budeš řešit nějaké maskování, tak na to třeba bude stačit změnit poslední parametr.
A teď ke kódu. Vůbec bych se nebál udělat to "klasicky". Vykreslovací tentononc se jmenuje Device Context (DC). V Delphi je schovaný v Canvasu, ale pořád tam je :smile1: . Když chceš dělat double buffering, tak si potřebuješ vytvořit takový DC v paměti. To se dělá přes funkci CreateCompatibleDC (kompatibilní DC s DC obrazovky, tiskárny nebo jiného zařízení). Aby se dalo do DC vykreslovat, tak musí mít přidělenou oblast/paměť, do které se kreslí. Taková oblast je bitmapa, která se DC musí přidělit (SelectObject). Nejlepší je vytvořit opět kompatibilní bitmapu s DC přes CreateCompatibleBitmap. Následně jen přidělíš a kreslíš. Takže bych ten tvůj kód vytvořil následovně:
- definuju si 2 proměnné
Vykres : TCanvas;
Bitmapa : HBITMAP;
- vytvořím si v OnCreate kompatibilní DC, bitmapu, "spojím je" a všechno to přidělím Canvasu (hdc : HDC):
Vykres := TCanvas.Create;
hdc := CreateCompatibleDC(Form1.Canvas.Handle);
bitmapa := CreateCompatibleBitmap(Form1.Canvas.Handle, form1.Width, form1.Height);
SelectObject(hdc, bitmapa);
Vykres.Handle := hdc;
- v OnPaint nechám BitBlt:
bitblt(form1.Canvas.Handle,0,0,40,40,vykres.Handle,0,0, SRCCOPY);
- v reakci na stisk tlačítka nechám vykreslení kružnice do paměti, ale pro jistotu přidám Repaint:
Vykres.Brush.Color:=clyellow;
vykres.Ellipse(0,0,40,40);
Repaint();
Co bude ještě nutné změnit? Vytvořená bitmapa je prázdná (černá), takže pokud nechceš černé pozadí, tak budeš muset přes vyplnit jinou barvu (FillRect aktuálním Brush). A ještě bych velikost vytvořené bitmapy neodovzoval od aktuální velikosti formuláře, ale třeba od velikosti pracovní plochy, protože ta bitmapa je tak velká, jak ji vytvoříš a za hranici nenakreslíš ani pixel :smile1: .
liborb, hafo diky, z tvého přispěvku jsem konečně dobře pochopil co vlastně dělám, moc diky :). Teď už mi to funguje. Respektive jsem zatim jenom u tohohle. Velikost te bitmapy je už drobnost.
Takže později až budu chtěl rozhybat kuličku tak vždycky budu muset překreslit jeji puvodni pozici kuličkou barvy jako pozadi a nakreslit ji o ten kousiček vedle? stejně tak i s nepřátelskými kuličkami?
Předpokládám že s plošinkami nebudde nejmenši problem, jen nakresleni do tohoto canvasu...
Přesně tak, když chceš nakreslit kuličku (i nepřátelskou) na nové pozici, tak ji na staré musíš smazat a nakreslit na nové. A ano, všechno musíš (stejně jako kuličky) kreslit do toho tebou vytvořeného DC, tj. do Canvasu.
Je hodně způsobů, jak se na to můžeš dívat. Třeba, že všechno co se nehýbá je pozadí. Případně si na to vytvořit bitmapu .... Možností je spousty. Kdybych ti směl poradit, tak v první kroku to dělej nejjedodušeji a všechno znovu, nebo-li:
- smazat komplet celý Canvas (DC)
- vykreslit kuličky
- vykreslit plošinky
- vykreslit ....
zavolat Repaint, aby se provedlo OnPaint.
Sazmořejmě to bude pomalé (ale ne zase tolik) a je to možné optimalizovat až do omrzení (překreslovat pouze regiony atd.). Ovšem pro začátek je to to nejlepší - když všechno vidíš a máš to pod kontrolou.
Přidej příspěvek
Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku
×Vložení zdrojáku
×Vložení obrázku
×Vložení videa
Uživatelé prohlížející si toto vlákno
Podobná vlákna
Plynuly prechod farieb — založil Tomáš Fedor
Plynuly pohyb textu v jQuery — založil Bazzi
Posun jmen — založil David
Posun Jmen — založil Radek
Posun v abecedě — založil TomBar