Lista Duplamente Encadeada
Uma lista duplamente encadeada é uma estrutura dinâmica onde cada nó armazena não apenas uma referência ao próximo elemento, mas também ao anterior. Isso permite percorrer a lista em ambas as direções com eficiência. Um nó típico pode ser definido em C como:
struct NoDuplo {
int valor;
struct NoDuplo* anterior;
struct NoDuplo* proximo;
};
Essa estrutura facilita operações como inserção e remoção em qualquer posição, especialmente útil em cenários onde há necessidade frequente de navegação reversa.
Cálculo da Matriz Adjunta
A matriz adjunta (ou clássica) de uma matriz quadrada é obtida ao transpor a matriz de cofatores. Para calcular:
- Encontre o cofator de cada elemento.
- Construa a matriz de cofatores.
- Transponha essa matriz.
Essa operação é fundamental para obter a inversa de uma matriz, sendo amplamente utilizada em transformações geométricas e soluções de sistemas lineares em aplicações gráficas.
Sintaxe Shader no Unity
Shaders no Unity são escritos usanddo a linguagem HLSL (High-Level Shading Language), embutida em blocos CGPROGRAM. Um exemplo básico inclui definição de propriedades, vértices e fragmentos:
Shader "Custom/Diffuse" {
Properties {
_MainTex ("Textura", 2D) = "white" {}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f {
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
}
Fluxo de Renderização de Shader no Unity
O pipeline gráfico no Unity envolve múltiplas etapas: processamento de vértices, rasterização e processamento de fragmentos. O shader de vértice transforma coordenadas locais para o espaço da câmera; os atributos interpolados são então passados ao shader de fragmento, que calcula a cor final por pixel. Essa divisão permite controle fino sobre iluminação, texturas e efeitos visuais.
Iluminação por Vértice vs por Fragmento
No sombreamento por vértice (Gouraud), os cálculos de luz ocorrem nos vértices e os resultados são interpolados entre eles. Embora mais rápido, isso pode gerar artefatos em superfícies curvas. Já no sombreamento por fragmento (Phong), a iluminação é calculada individualmente para cada fragmento, resultando em gradientes mais suaves e realistas, ao custo de maior carga computacional na GPU.
Produto Escalar e Vetorial de Vetores
- Produto escalar (dot product): Indica o quanto dois vetores são semelhantes. Quando normalizados, seu valor varia entre -1 e 1, represantando o cosseno do ângulo entre eles. Também é usado para projeções ortogonais.
- Produto vetorial (cross product): Gera um vetor perpendicular ao plano formado pelos dois vetores originais. É essencial para calcular normais de superfície e orientação em 3D.
- Vetores normalizados: Utilizados quando apenas a direção importa, como em cálculos de luz e movimento.
Reflexão de Raios em Superfícies
Dado um vetor de incidência I e uma normal unitária N, o vetor refletido R pode ser calculado como:
R = I - 2 * dot(N, I) * N
Essa fórmula deriva da decomposição do vetor de entrada em componentes paralela e perpendicular à normal, ivnertendo apenas a componente incidente.
Ordenação Rápida: Implementação e Análise
O algoritmo QuickSort utiliza divisão e conquista para ordenar coleções:
void OrdenarRapido(int[] dados, int inicio, int fim) {
if (inicio >= fim) return;
int pivo = dados[inicio];
int esquerda = inicio, direita = fim;
while (esquerda < direita) {
while (esquerda < direita && dados[direita] >= pivo)
direita--;
dados[esquerda] = dados[direita];
while (esquerda < direita && dados[esquerda] <= pivo)
esquerda++;
dados[direita] = dados[esquerda];
}
dados[esquerda] = pivo;
OrdenarRapido(dados, inicio, esquerda - 1);
OrdenarRapido(dados, esquerda + 1, fim);
}
O melhor caso ocorre quando o pivô divide o array ao meio, resultando em complexidade O(n log n). O pior caso, com pivôs extremos, leva a O(n²), típico em listas já ordenadas. Em média, o desempenho permanece próximo do ideal.
Simulação de Classes em Lua
Lua não possui classes nativas, mas permite simular comportamentos orientados a objetos usando tabelas e metatables:
local Pessoa = {}
Pessoa.__index = Pessoa
function Pessoa:new(nome, idade)
local inst = setmetatable({}, self)
inst.nome = nome
inst.idade = idade
return inst
end
function Pessoa:saudar()
print("Olá, sou " .. self.nome)
end
-- Uso
local joao = Pessoa:new("João", 25)
joao:saudar()
Otimização de Scripts Lua em Jogos
Para melhor desempenho, recomenda-se o uso do LuaJIT, que compila código Lua diretamente para linguagem de máquina. Porém, plataformas como iOS restringem a execução de código gerado dinamicamente por questões de segurança, forçando o uso do modo interpretado. Nessas situações, o código é convertido em bytecode e executado via switch interno, mantendo portabilidade, mas com menor velocidade. Estratégias como evitar alocações desnecessárias, usar tabelas pré-alocadas e minimizar chamadas entre Lua e C# ajudam a manter a fluidez em tempo de execução.