Fórum [Jogos] Metodo para colisão de multiplos objetos #375007
17/09/2009
0
A pedido e a meu interesse tambem, venho a compartilhar um metodo de colisão entre objetos. E tambem discutir sobre metodos para colisão.
Acho que isso irá envolver muita coisa além de colisão de objetos.
A principio Aqui há um simples metodo de se verificar a colisão entre dois objetos:
Usei 2 TPanel, apenas como exemplo.
unit teste;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
BT1: TPanel;
BT2: TPanel;
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
function colisao: Boolean;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
function TForm1.colisao: Boolean;
begin
Result := True;
if ((BT1.Left > BT2.Left + 40) or
(BT1.Left + 40 < BT2.Left) or
(BT1.Top > BT2.Top + 40) or
(BT1.Top + 40 < BT2.Top)) then
Result := False;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if getkeystate(vk_left) < 0 then bt1.Left := bt1.Left-1;
if getkeystate(vk_Right) < 0 then bt1.Left := bt1.Left+1;
if getkeystate(vk_up) < 0 then bt1.top := bt1.top-1;
if getkeystate(vk_down) < 0 then bt1.top := bt1.top+1;
if colisao then
form1.Canvas.TextOut(10, 40, ´colidiu´)
else form1.Canvas.TextOut(10,40,´ ´);
end;
end.Créditos a Robotizar do PdJ.
Este modelo podemos verificar a colisão vendo como um plano cartesiano, onde pegamos a posição x e y e usamos o seu tamanho horizontal e vertical para testar suas faces na colisão.
Usando mapa (titles), podemos verificar a colisão de acordo com a posição das peças na array.
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 1 1 0 0
Temos aqui o mapa dos titles onde podemos verificar a colisão de acordo com o valor da posição.
if not (array[x][y] = Peca.X+1) then Peca.X + 1;
Explicando o codigo acima... O 2 seria a peça a mover, onde verificamos, se no mapa na posição x e y (pra onde a peça vai moder ´Peca.X+1´), existe algum objeto. Onde 1 há objeto, 0 não há objeto.
Indo direto ao assunto do tópico, um método para verificar a colisão de multiplos objetos. Estarei elaborando o código para colisão de multiplos e dando um exemplo do mesmo. Assim que estiver pronto, Eu venho a postar para a comunidade.
Deixo uma pergunta, Sabem como pegar a coleção de objetos que já foi criado pela factory!? Fiquei pensando nisso para a criação deste método.
Critiquem avontade.
.lg.
Curtir tópico
+ 0Posts
17/09/2009
Osocram
Sobre como pegar os objetos criados....
No seu factory ao criar cada objeto faça ele associar este objeto a um TList.
Caso não conheca um TList seria similar ao TStringList, mas como pode ver STringList é lista de string, e apenas List é uma lista.. lista do que? Eu lhe respondo... Lista de Ponteiros.
Ohh ponteiros.. então posso associar qualquer tipo de objeto nele?
Sim, vc pode.
Bom sobre o outro assunto, colisão.
Não sei como esta fazendo mas eu ja mexi com isso tbm, e fazia assim...
Todos objeto tinha uma propriedade Move;
Então tinha uma especie de timmer na tela principal, a cada intervalo varria essa lista que tinha os objetos e disparava o move de cada um.
Ao disparar isso.. cada objeto ja via se ao se mover acontecia uma colisão.
Dae cada Objeto tem um evento OnColide nesse evento colocava o que tinha que acontecer qdo acontecer uma colisão.
Assim usava bem a OOP que é o objeto tem as suas responsabilidades. Ele mesmo sabe ver se colidiu, ele mesmo sabe o que deve fazer para se mover, sabe o que fazer qdo colidir e tudo mais.
Mexer com jogos, usa-se mto OOP.
flw
Gostei + 0
17/09/2009
Marcosrocha
Q = Resultado da equação
Q1 = Raio circunferência 1
Q2 = Raio circunferência 2
Tendo uma circunferência de diâmetro 15 pixels, temos:
Q = (7.5 + 7.5) * (7.5 + 7.5)
Conclusão: detectamos a colisão entre duas esferas com um teste simples.
Q1 := Power(Bola2.Left - Bola1.Left, 2); Q2 := Power(Bola2.Top - Bola1.Top, 2); if Q1 + Q2 <= (7.5 + 7.5) * (7.5 + 7.5) then ShowMessage(´Houve colisão´);
Gostei + 0
18/09/2009
.lg.
MarcosRocha, obrigado pela contribuição.
Gostei + 0
18/09/2009
Osocram
Vc ter um Objeto vamos chamar de AreaPrincipal, seria onde todo o jogo vai acontecer, ter a area de fundo, onde os sprites vai aparecer. Fisicamente falando seria um TImage (falei isso para uma melhor visualização).
Nesse objeto vc teria um propriedade MeusObjetos: TList; DoMove (onde vc vai fazer um loop no TList e executar o Move de cada objeto).
Assim podemos manter organizado a estrutura de cada objeto e cada um com a sua responsabilidade.
Gostei + 0
18/09/2009
Knight_of_wine
Com ele você pode renderizar todos os seus objetos de tela sem problemas.
Eu estou fazendo um pequeno projeto que tem todas essas técnicas, só que estou usando o XNA Framework, então é bem diferente de programar em Delphi, pois grande parte das classes estão prontas e no caso de colisões posso usar simplesmente o método Intersect de dois retangulos definidos pela área de cada sprite.
Mas grande parte da lógica é bem parecida.
Eu fiz algo em Delphi também e no caso de colisão entre dois objetos, fiz um método mais ou menos como o seu do Panel, mas usava TImageList, pois eram sprites animados.
Gostei + 0
18/09/2009
Marcosrocha
procedure TForm1.DoGameDraw; begin ... // Limpa backbuffer preenchendo-o de preto ... // Calcula posições dos objetos ... // Desenha novas posicoes no Backbuffer // Desenha o Backbuffer no Form BitBlt(Form1.Canvas.Handle, 0, 0, Backbuffer.Width, Backbuffer.Height, Backbuffer.Canvas.Handle, 0, 0, SRCCOPY); end
type TcBola = class(TPersistent) public Left, Top, Width, Height: Integer; constructor Create(Left, Top, Width, Height: Integer); end; ... var Form1: TForm1; ListBolas: TList; procedure TForm1.FormCreate(Sender: TObject); begin ListBolas := TList.Create; // Adicionando objetos na TList ListBolas.Add(TcBolas.Create(0,0,10,10)); ListBolas.Add(TcBolas.Create(150,150,10,10)); ListBolas.Add(TcBolas.Create(250,250,25,25)); // Acessando objetos na TList TcBolas(ListBolas[0]).Top := 5; TcBolas(ListBolas[0]).Left := 5; TcBolas(ListBolas[1]).Top := 10; TcBolas(ListBolas[1]).Left := 10; end; procedure TForm1.FormDestroy(Sender: TObject); begin FreeAndNil(ListBolas); end;
Gostei + 0
18/09/2009
Marcosrocha
Gostei + 0
18/09/2009
Osocram
Eu usei algo parecido com isso que vc postou.
acho que so esqueceu de um detalhe.
isso aqui so vai liberar a lista da memoria... mas os objetos associados a ela ainda estaram em memoria.
procedure TForm1.FormDestroy(Sender: TObject); begin FreeAndNil(ListBolas); end;
O interessante seria fazer um loop liberando os objteos e no fim liberar a lista.
algo do tipo
//aqui o loop p liberar os objetos for i:=0 to ListBolas.Count -1 do begin if assigned(listBolas[i]) then FreeAndNil(listBolas[i]) end; // aqui depois q liberou os objetos, agora é so liberar a list FreeAndNil(ListBolas);
Senão fizer isso pode gerar MemoryLeak.
Gostei + 0
18/09/2009
Osocram
Eu tinha feito um jogo de nave, no qual ele tinha um formata triangular e nas asas uns canhões.
Então eu calculava todos os pontos em volta da minha nave para deixar uma colisão mais perfeita, e fiz isso com os inimigos tbm. (minha colisão era um poligono)
Um amigo meu me ensino que deveria fazer camadas de colisão.
Por exemplo a primeira colisão seria uma figura simples como uma circulo ou um quadrado. Quem o amigo MarcosRocha passou uma formula simples de achar colisão usando circulos, então o circulo seria a colisão maior.depois eu faria colisões de caixa pegando partes do meu sprite (usando varios retangulos) e por ultimo a minha colisão de poligono.
Explicando o Circulo seria maior de todos, e facil de testar, então eu iria deixar de lado por enqto as outras colisão (sem precisa calcular eles ainda pois se nem passou do circulo não chegou neles). Passando do circulo vc começa a testar as caixas de colisão, passando da caixa dae começa a testar o poligono.
Com isso vc economiza mtoooooooooo calculos.
Espero ter conseguindo explicar.
Gostei + 0
18/09/2009
Marcosrocha
Eu tinha mais alguma dica para dar mas acabei deixando pra depois e esqueci. Depois que eu lembrar eu volto e posto.
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)