Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Boa tarde turma!
O problema (literalmente, matemático) é o seguinte:
Preciso calcular o incremento de experiência e elevação de níveis num game que estou desenvolvendo.
Já tenho uma função, contudo, no estado atual, ele apenas registra o avanço de um nível por chamada. Daí me deparei com um problema: em momentos será possível subir mais que um level ao adicionar um grande montante de experiência então preciso de ajuda pra melhorar esta função.
Função atual:
function companhiaExperiencia( $companhia = 0, $adicionar = 0 ) { global $_DB, $_MOD;
// coleta
$sql = 'SELECT reg, nivel, nivelExp, nivelProximo FROM '.$_MOD['companhias']['tabela'].' WHERE reg = '.intval($companhia);
$con = $_DB->consulta($sql);
$_companhia = $_DB->registro();
if ( !$adicionar ) return $_companhia;
// variaveis
$novoLevel = false;
$nivel = $_companhia['nivel'];
$nivelExp = $_companhia['nivelExp'] + $adicionar;
$nivelProximo = $_companhia['nivelProximo'];
// atualizacao
if ( $adicionar )
{ if ( $nivelExp >= $_companhia['nivelProximo'] )
{ $nivel++;
$nivelProximo += COMPANHIA_Experiencia_Base + porcentagem($nivelProximo, COMPANHIA_Experiencia_Taxa);
$novoLevel = true;
}
$sql = 'UPDATE '.$_MOD['companhias']['tabela'].' SET nivel = '.$nivel.', nivelExp = '.$nivelExp.', nivelProximo = '.$nivelProximo.' WHERE reg = '.intval($companhia);
$con = $_DB->consulta($sql);
}
return array('nivel' => $nivel, 'nivelExp' => $nivelExp, 'nivelProximo' => $nivelProximo, 'novoLevel' => $novoLevel);
}Como podem ver, ela apenas registraria um level, para atualizar eu precisaria fazer outra, e mais outra..., camada a função e , claro, não queremos isto. ^^
Alguma ideia para resolver meu problema? Luzes?
Veja que chamamos [inline]novoNivel[/inline] antes de chamar [inline]adicionaExp[/inline]. É importante observar isso para que a função não entre em loop infinito.
Opa @Evandro.
Ainda assim não resolve o problema pois não é interessante para a aplicação por a função em loop, várias chamadas.
Mesmo assim, obrigado pela ideia.
Ideia:
Estive pensando em fazer uma espécie de tabela com uma relação entre níveis e suas respectivas experiências necessárias.
Estou tentando bolar aqui... Ajuda?
Edit: eu já tenho um valor base para cada level e um acréscimo (porcentagem) definidos, veja as constantes.
A ideia deu certo:
function companhiaExperiencia( $companhia = 0, $adicionar = 0 ) { global $_DB, $_MOD;
// coleta
$sql = 'SELECT reg, nivel, nivelExp, nivelProximo FROM '.$_MOD['companhias']['tabela'].' WHERE reg = '.intval($companhia);
$con = $_DB->consulta($sql);
$_companhia = $_DB->registro();
if ( !$adicionar ) return $_companhia;
// variaveis
$novoLevel = false;
$nivel = $_companhia['nivel'];
$nivelExp = $_companhia['nivelExp'] + $adicionar;
// tabela de experiencia necessaria por level
$_niveis = array();
$n = $nivel + 5; // projetando o array para n niveis alem do atual
$nProximo = 0;
for($i = 1; $i <= $n; $i++) {
$nProximo += round(COMPANHIA_Experiencia_Base + porcentagem($nProximo, COMPANHIA_Experiencia_Taxa)); // $nProximo + 10000 + x% de $nProximo
$_niveis[$i] = $nProximo;//...
}
Saída:
>
Array( [1] => 10000 [2] => 20500 [3] => 31525 [4] => 43101 [5] => 55256 [6] => 68019 [7] => 81420 [8] => 95491 [9] => 110266)
Próximo passo:
Agora preciso de uma função ou forma de pegar o valor minimamente mais aproximado possível da experiência atual dentro do array.
Alguém conhece alguma função que faça isto?
Ex: expAtual = 60000, preciso retornar a key 5.
Se quiser procurar por você mesmo, nas atualizações de status minhas, do @JCMais e, ou do ou do @Vinicius Rangel (eu sempre confundo os dois) tem uma discussão sobre formas de obter um valor aproximado dentre os disponíveis num array, com códigos inclusive.
Porém, a alternativa do Evandro é melhor porque num jogo que, normalmente, é virtualmente infinito, imagine o cenário daqui uns anos, quando a lista de XP for bem extensa (de zero a mil, para ilustrar). Você vai mesmo computar a proximidade em todo Level Up, para todos os jogadores cadastrados?
E mais! Você não acha interessante entrar em loop para isso... Isso só se tornaria um gargalo perceptível se você permitisse ao jogador "upar" 500 níveis ou mais de uma só vez.
Como a tendência de uma curva evolutiva é sempre se tornar cada vez maior, mesmo nos primeiros níveis em que os pontos de encontro são bem próximos, você vai ter (ou deveria ter) no máximo uns 4 ou 5 Level Up simultâneos.
E uma iteração de 5 passos não é grande coisa.
Não vi esse loop do qual ele está falando, ou recursividade é considerado um loop também? De qualquer forma, não tem outro jeito para resolver isso aí.
Quanto a subir mais de um nível de uma só vez, creio que um game que permita isso está um tanto mal projetado. Subir de nível tem de ser algo mais trabalhoso, não? Nunca vi um jogo no qual o jogador pode subir mais de um nível de uma só vez...
>
Se quiser procurar por você mesmo, nas atualizações de status minhas, do @JCMais e, ou do ou do @Vinicius Rangel (eu sempre confundo os dois) tem uma discussão sobre formas de obter um valor aproximado dentre os disponíveis num array, com códigos inclusive.
Porém, a alternativa do Evandro é melhor porque num jogo que, normalmente, é virtualmente infinito, imagine o cenário daqui uns anos, quando a lista de XP for bem extensa (de zero a mil, para ilustrar). Você vai mesmo computar a proximidade em todo Level Up, para todos os jogadores cadastrados?
E mais! Você não acha interessante entrar em loop para isso... Isso só se tornaria um gargalo perceptível se você permitisse ao jogador "upar" 500 níveis ou mais de uma só vez.
Como a tendência de uma curva evolutiva é sempre se tornar cada vez maior, mesmo nos primeiros níveis em que os pontos de encontro são bem próximos, você vai ter (ou deveria ter) no máximo uns 4 ou 5 Level Up simultâneos.
E uma iteração de 5 passos não é grande coisa.
Entendido Bruno!
Levando em conta sua valiosa observação, obrigado, mais ainda sim rejeitando a chamada em loop da função primaria, acho que tenho um possível avanço: tornar este array (com a relação de niveis => expNecessaria) global o lançando na sessão!
Assim, ao custo de um pouco de memória, evito as chamadas em loop e a repetição da formação da matriz.
O que acham?
>
Não vi esse loop do qual ele está falando, ou recursividade é considerado um loop também? De qualquer forma, não tem outro jeito para resolver isso aí.
Quanto a subir mais de um nível de uma só vez, creio que um game que permita isso está um tanto mal projetado. Subir de nível tem de ser algo mais trabalhoso, não? Nunca vi um jogo no qual o jogador pode subir mais de um nível de uma só vez...
Imagine a seguinte situação:
- a companhia está a 100xp do level 10;
- e necessário mais 10.100 para o level 11;
Este lhe parece um cenário possível que justifica minha preocupação?
Estou preocupado em cobrir todos os cenários possíveis, como, acredito, deve ser. Concorda?
>
Imagine a seguinte situação:
- a companhia está a 100xp do level 10;
- e necessário mais 10.100 para o level 11;
Este lhe parece um cenário possível que justifica minha preocupação?
Estou preocupado em cobrir todos os cenários possíveis, como, acredito, deve ser. Concorda?
Não... para subir de nível, é preciso que os jogadores encarem ao menos mais de uma missão (especiais ou não...) ou vão ficar subindo de nível a todo momento... Nenhuma missão deve dar XP suficiente para subir um nível apenas com eles.
Por favor, poste aqui a tabela dos níveis e quantidades de XP necessários para alcança-los nesse seu jogo.
Fui infeliz no nome. Apesar de poder considerar recursividade um loop - se falarmos de linhas ...
function myDemonstration() {
myDemonstration(); // go to line 1
}
Como já foi dito, armazenar a tabela de experiência em qualquer mecanismo de persistência pode vir a ser inviável um dia. O mais correto é criar uma função que armazene a fórmula. Coisa que, de fato, você já tem
>
function companhiaExperiencia( $companhia = 0, $adicionar = 0 ) {
for($i = 1; $i <= $n; $i++) {
$nProximo += round(COMPANHIA_Experiencia_Base + porcentagem($nProximo, COMPANHIA_Experiencia_Taxa)); // $nProximo + 10000 + x% de $nProximo
$_niveis[$i] = $nProximo;
} //d($_niveis,1); // debug da var
}
function requiredExp($nProximo) {
$percent = porcentagem($nProximo, COMPANHIA_Experiencia_Taxa);
return round(COMPANHIA_Experiencia_Base + $percent);
}
Estou supondo que você sabe quanto de experiência a companhia tem e qual o nível dela:
function addExp($amount) {
$requiredExp = requiredExp($companyLevel + 1);
$amount -= $requiredExp - $companyExp;
if ($amount >= 0) {
// update companies set level += 1
addExp($amount);
}
// update companies set exp += $amount
}Levando em conta sua valiosa observação, obrigado, mais ainda sim rejeitando a chamada em loop da função primaria, acho que tenho um possível avanço: tornar este array (com a relação de niveis => expNecessaria) global o lançando na sessão!
Além de você estar fazendo a coisa errada você está trocando seis por meia dúzia. Não importa onde você armazenaria o range de XP, até do seu armazenamento você teria de computá-lo.
Claro, você pode otimizar pré-determinando um range bem confortável (200 niveis, digamos assim) e armazenando num array estático. Você não faz a coisa errada por armazenar em sessão, mas cada modificação que você venha a fazer na fórmula terá de recriar toda a matriz.
E mesmo que não se utilize de Orientação a Objetos, lateralizar o conceito de O.C.P. é bastante válido.
E a graça da programação é automatização. Criar um algorítimo matemático flexível o suficiente para ser adaptável pelas escolhas do usuário e uma única rotina (relativamente falando) que utilize tal algorítimo.
Assim, ao custo de um pouco de memória, evito as chamadas em loop e a repetição da formação da matriz.
Quanto mais você puder melhorar a performance melhor, claro! Mas arrays são malignos em termos de performance.
Claro, o exemplo do artigo é muito além do contexto desse tópico, mas como eu disse, um jogo é algo virtualmente infinito.
O melhor dos algorítimos que eu vi para computar um valor numérico aproximado numa lista estática de valores, por si só, envolve um loop e alguns cálculos matemáticos e lógicos.
Não vi esse loop do qual ele está falando, ou recursividade é considerado um loop também? De qualquer forma, não tem outro jeito para resolver isso aí.
Sim, recursividade é um loop disfarçado. Uma coisa que acho muito maneira mas não entendo absolutamente nada é recursar sem recursividade por utilizar referências (um snippet que vi nos comentários do manual de DirectoryIterator se não me engano).
Mas não vem ao caso...
Quanto a subir mais de um nível de uma só vez, creio que um game que permita isso está um tanto mal projetado. Subir de nível tem de ser algo mais trabalhoso, não? Nunca vi um jogo no qual o jogador pode subir mais de um nível de uma só vez...
Veja como exemplo Skyrim, da Bethesda Softworks. Dentre todos os recursos existem as habilidades separadas em três grupos: Combate, mágica e uma terceira que me foge a palavra apropriada eferente às habilidades de um ladrão.
Vou usar como exemplo as habilidades de ladrão. Uma delas é abrir fechaduras com uma gazua (uma "mixa"). Existem diversos níveis de dificuldade de trancas cada uma que, ao serem abertas, proporcionam quantidades maiores de XP.
Se logo no início você encontrar uma fechadura de nível "Master" (a mais difícil existente - sério :o), e tiver a sorte ou habilidade de abrí-la, você conseguirá muito mais XP do que um único nível na habilidade nomeada "Lockpicking" suporta.
E como cada vez que uma habilidade aumenta de nível o personagem também ganha XP, dessa forma, você (personagem) evolui mais de uma vez após uma única ação, pois a dita habilidade também evolui mais de uma vez só.
Eu adoro tópicos com história... ^_^
Claro, é questão de estratégia e marketing definir se haverá sucessivos Level Up dessa forma e creio que a empresa tenha pensado nisso, haja vista que é bastante penoso subir algumas habilidades do nível 1 ao 100.
>
Não... para subir de nível, é preciso que os jogadores encarem ao menos mais de uma missão (especiais ou não...) ou vão ficar subindo de nível a todo momento... Nenhuma missão deve dar XP suficiente para subir um nível apenas com eles.
Por favor, poste aqui a tabela dos níveis e quantidades de XP necessários para alcança-los nesse seu jogo.
Discordo no tocante a possibilidade disto ocorrer, observe que o mérito não é a permissão de que isto ocorra e sim prever a possibilidade de que isto ocorra e, assim sendo, tratar o evento. Mesmo assim, obrigado pelas críticas. ^^
>
Como já foi dito, armazenar a tabela de experiência em qualquer mecanismo de persistência pode vir a ser inviável um dia. O mais correto é criar uma função que armazene a fórmula. Coisa que, de fato, você já tem
function requiredExp($nProximo) {
$percent = porcentagem($nProximo, COMPANHIA_Experiencia_Taxa);
return round(COMPANHIA_Experiencia_Base + $percent);
}
Estou supondo que você sabe quanto de experiência a companhia tem e qual o nível dela:
function addExp($amount) {
$requiredExp = requiredExp($companyLevel + 1);
$amount -= $requiredExp - $companyExp;
if ($amount >= 0) {
// update companies set level += 1
addExp($amount);
}
// update companies set exp += $amount
}
Perfeito, função "enxugada", obrigado.
Agora quanto a armazenagem do array na sessão, vou me explicar melhor: usando a fórmula que já temos, uma vez apenas eu posso preenchê-la de 1 ao level atual da companhia + 10 (range de segurança) e, ao processar os ganhos de experiência posteriores ao início da sessão eu percorreria este array a partir do level atual da companhia até onde a adição de exp deixar.
Em fim! Isto seria, ou não, melhor que, recursivamente, chamar a função?
-------------------------------------------------- EDIT:
@Bruno, e isto ai!
Apesar da crítica inicial do "estar fazendo errado", rs, fico feliz que tenha entendido o objetivo: economizar ao máximo ao servidor pois, conforme suas palavras, independente do sucesso: "um jogo é algo virtualmente infinito".
Quanto a avançar 2+ níveis é justamente isto! É perfeitamente possível que aconteça tal como exemplificou.
Reitero:
Qual a melhor opção neste caso? O array estático na sessão ou chamar recursivamente a mesma função?
Vale lembrar que o objetivo, no qual esta a base de todo desenvolvimento, é performance.
Solução finalizada usando a sessão:
/***************
# EXPERIENCIA #
***************/
function companhiaExperiencia( $companhia = 0, $adicionar = 0 ) { global $_DB, $_MOD;
$sql = 'SELECT reg, nivel, nivelExp, nivelProximo FROM '.$_MOD['companhias']['tabela'].' WHERE reg = '.intval($companhia);
$con = $_DB->consulta($sql);
$_companhia = $_DB->registro();
if ( !$adicionar ) return $_companhia;
// variaveis
$novoLevel = false;
$companhiaNivel = $_companhia['nivel'];
$companhiaExp = $_companhia['nivelExp'] + $adicionar;
// tabela (na sessão) de experiencia necessaria por level
$nivelMaximo = $companhiaNivel + 10; // margem de seguranca
if ( !isSet($_SESSION['EXP']['companhias']['tabela']) )
{ $_niveis = array();
$expProximoNivel = 0;
for($i = 1; $i <= $nivelMaximo; $i++) {
$expProximoNivel += round(COMPANHIA_Experiencia_Base + porcentagem($expProximoNivel, COMPANHIA_Experiencia_Taxa)); // $expProximoNivel + 10000 + x% de $expProximoNivel
$_niveis[$i] = $expProximoNivel;
}
$_SESSION['EXP']['companhias']['tabela'] = $_niveis;
}
// verificacao de level up
$_niveis = $_SESSION['EXP']['companhias']['tabela'];
$proximoNivel = $_niveis[$companhiaNivel+1];
for($i = $companhiaNivel; $i <= $nivelMaximo; $i++) {
if ( ($companhiaExp - $_niveis[$i]) < 0 )
{ $companhiaNivel = $i-1;
$proximoNivel = $_niveis[$i];
$novoLevel = true;
break;
}
}
// atualizacao
$sql = 'UPDATE '.$_MOD['companhias']['tabela'].' SET nivel = '.$companhiaNivel.', nivelExp = '.$companhiaExp.', nivelProximo = '.$proximoNivel.' WHERE reg = '.intval($companhia);
$con = $_DB->consulta($sql);
return array('nivel' => $companhiaNivel, 'nivelExp' => $companhiaExp, 'nivelProximo' => $proximoNivel, 'novoLevel' => $novoLevel);
}
Subtraia a quantidade adicionada da quantidade que falta pro cara subir de nível. Se o resultado for positivo, faça a função chamar a ela mesma com a quantidade restante
Veja que chamamos [inline]novoNivel[/inline] antes de chamar [inline]adicionaExp[/inline]. É importante observar isso para que a função não entre em loop infinito.