[Jogos] Metodo para colisão de multiplos objetos
Ola delphianos.
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.
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.
Temos aqui o mapa dos titles onde podemos verificar a colisão de acordo com o valor da posição.
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.
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.
Curtidas 0
Respostas
Osocram
17/09/2009
amigo....
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
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
Marcosrocha
17/09/2009
Colisão entre circunferências ( 2D ) é dada pela soma dos quadrados das circunferências, logo temos a equação: Q = (Q1 + Q2)² onde:
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.Eu tenho um código começado para futuramente fazer jogos e etc. Quem quiser para testar colisões esféricas em 2D só me avisar.
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
.lg.
17/09/2009
osocram, obrigado. Estaja pensando como fazer isso, infelizmente não estava em meus pensamentos usar TList.
MarcosRocha, obrigado pela contribuição.
MarcosRocha, obrigado pela contribuição.
GOSTEI 0
Osocram
17/09/2009
Eu acho que um bom jeito seria assim.
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.
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
Knight_of_wine
17/09/2009
Pra isso seria interessante usar TPaintBox.
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.
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
Marcosrocha
17/09/2009
Eu aprendi usando uma variável TBitmap que chamamos de Backbuffer e quando todo o jogo, movimentos, e etc estão desenhados nele eu jogo para o Canvas do Form. Particularmente, acho o PaintBox primitivo demais para trabalhar. O resultado com backbuffer fica algo parecido como:Pra quem nunca usou TList. Segue um exemplo:Claro que aqui foi um exemplo bobo, pois os objetos possuem somente Left, Top, Width e Height. Mas é esse o conceito.
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
Marcosrocha
17/09/2009
Uma coisa que eu esqueci... Tente sempre utilizar BitBlt ao invés de Canvas.CopyRect, isto porque o CopyRect faz inúmeros cálculos antes de desenhar e o BitBlt é extremamente mais rápido, até mesmo para copiar grandes áreas de imagens.
GOSTEI 0
Osocram
17/09/2009
Isso mesmo Marcos Rocha.
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.
O interessante seria fazer um loop liberando os objteos e no fim liberar a lista.
algo do tipo
Senão fizer isso pode gerar MemoryLeak.
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
Osocram
17/09/2009
Outro metodo de calcular colisão que aprendi depois de um tempo... que melhorou mto a performance.
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.
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
Marcosrocha
17/09/2009
Realmente osocram. Fiquei com receio de adicionar o for no FormDestroy para limpar os objetos da TList, porém é isso que deve ser feito.
Eu tinha mais alguma dica para dar mas acabei deixando pra depois e esqueci. Depois que eu lembrar eu volto e posto.
Eu tinha mais alguma dica para dar mas acabei deixando pra depois e esqueci. Depois que eu lembrar eu volto e posto.
GOSTEI 0