Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mensajes - BssString

46
Hola Taki-A

1 - Mencionaste que al crear la bala "arriba" del enemigo hace que la bala sólo lo persiga en el eje X y no alcanza a hacer colisión. Eso pasa porque la Bala se dirige hacia las coordenadas de origen del sprite, si tu sprite enemigo tiene el origen en la coordenada (x=0, y=0) obiamente la bala irá hacia ese punto.
Para ir al centro, lo que puedes hacer es cambiar la última línea que te calcula la nueva dirección de la Bala:
direction = point_direction(x,y,next_target.x,next_target.y)
Por esta nueva línea:
direction = point_direction(x,y,next_target.x+next_target.sprite_width/2,next_target.y+next_target.sprite_height/2)
No es la mejor solución, porque va a variar según el tamaño del sprite, pero si no quieres cambiar la coordenada de origen, puede ser una buena solución parche a ese problema.

2 - Lo otro que comentaste, es que sólo detecta un enemigo y no los demás.
Eso es porque la función "with (obj_enemigo_general)" no los está tomando en cuenta. Comprobaste que todos los objetos enemigos tengan asignado como parent al obj_enemigo_general???
Por alguna razón esa función no los está tomando en cuenta.

Saludos
47
Preguntas y respuestas / Re:Error al cargar el juego
Febrero 05, 2020, 01:43:56 AM
Hola Kalimerodrag

Que extraño. No sé que plataforma o sistema operativo estés usando, pero al menos yo uso Windows y sé que Game Maker Studio no puede acceder a las carpetas de tu ordenador, sólo puede acceder a ciertas carpetas, de hecho por defecto se crean y cargan los archivos desde Local/Appdata. Puedes acceder al directorio si buscas %localappdata% en el buscador de Windows.

Si has cambiado el archivo al escritorio e intentas cargar ejemplo: load_game("Desktop/game.sav") no te va a funcionar aunque el archivo exista porque Game Maker no tiene acceso a "Desktop".

Para que Game Maker gane acceso a cualquier carpeta del ordenador, el jugador le tiene que dar acceso manualmente mediante las funciones "get_open_filename" o "get_save_filename".

Intenta esto para cargar tu juego:
[gml]var file; file = get_open_filename("Continuar", "")
if file != "" load_game(file)[/gml]

Eso te abrirá un cuadro de díalogo (los tipicos cuando pulsas "File" > "Open" en Windows) que te permitirá buscar el archivo en tu ordenador y cargarlo manualmente a Game Maker.

Saludos
48
Hola Taki-A

Sigo sin entender bien tu problema, probé el código que me pasaste y me funciona perfecto.

Cuando la bala colisiona con cualquiera de los hijos, se cambia de dirección hacia el próximo enemigo más cercano (que no haya tocado antes)
Puse 10 enemigos (5 de un objeto y 5 de otro con un padre en común) y la bala rebotó en los 10 enemigos y a todos los quitó HP, y cuando llegó al décimo enemigo siguió su trayectoria al infinito porque ya no encontró más enemigos NO golpeados para atacar. Para evitar que la bala recorra hasta el infinito, creale una alarma en el "create event" que le de un cierto tiempo de vida, luego en el evento de activación de la alarma destruyes la instancia del proyectil, así ya no viajará hasta el infinito.

Si la bala no está causando daño, primero dibuja en pantalla la variable "vida" de cada enemigo para ver cómo se comporta cuando recibe la bala.

Mi sugerencia es que revises en los objetos hijos si TODOS tienen asignado el "obj_enemigo_general" como padre, si uno de los hijos no tiene asignado ese padre, entonces quedan fuera de la detección.

También revisa que tanto el proyectil como todos los sprites de los objetos hijos tengan una máscara de colisión ya que el check de colisión se hace contra el cuadro de colisión de los sprite.

Ojo que la dirección que gana la bala se calcula en base al punto de origen de los sprites. Si tienes el punto de origen en la coordenada ( 0, 0) puede ser que la bala pase por una parte del Sprite transparente y por eso no te detecta la colisión.

A modo de test, puedes escribir un "show_message" (o un show_debug_message) justo debajo de la línea que le quita vida a los enemigos para que aparezca cada vez que la bala detecta colisión, así sabrás si el problema es que la bala no colisiona u otra cosa.
ejemplo:
show_message("La bala colisiona con: "+object_get_name(other.object_index))

Saludos Cordiales
49
Hola FernandoNavarro

Para eso existe la función "string_pos(substr, str)".
Te devuelve el número de la posición donde encontró tu texto "substr" dentro de la string "str" o devuelve cero si no lo encuentra.

Ejemplo:
string_pos("H","Hola mundo")
Te devuelve "1" porque encontró la "H" en la primera posición

Ejemplo 2:
string_pos("chao","Hola mundo")
Te devuelve "0" porque No encontró la palabra "chao" dentro de tu String.

Saludos
50
Qué rato, yo lo testié y me funcionó perfecto.

Manda tu código para ver cómo lo has dejado y dime en qué objeto y en qué evento has puesto el código.
Con el código puedo ver también el nombre de las variables que usas
51
Hola Taki-A

El código que coloqué era sólo para cambiar la trayectoria de la bala, no para moverla.

Intenta asignarle una variable "speed" en el create event para que la bala se mueva.

Saludos
52
Hola Taki-A

Eso lo puedes hacer llamando una función "with" desde el proyectil para encontrar al enemigo más cercano.
Si no quieres repetir el enemigo para que cada rebote golpee a un objetivo único, puedes añadir el ID del objeto que ya fue golpeado a una "data structure" e ignorarlo del check. (Creo que así funcionan las balas de Jessie)

Ejemplo de lo que digo.

En el create event del proyectil:
[gml]objetivos_golpeados = ds_list_create() //Esto crea una DS_LIST que guardará el ID de los objetivos golpeados para ignorarlos en el check del rebote y colisión[/gml]

Importante eliminar la DS LIST cuando el proyectil se destruye y cuando se termina la ejecución del Juego (por si cierras el juego antes que el proyectil se destruya).
En el "Game End Event" y en el "Destroy Event"
[gml]if ds_exists(objetivos_golpeados,ds_type_list) ds_list_destroy(objetivos_golpeados) //Esto elimina la DS LIST de la memoria[/gml]
Yo uso GMS1.4 y en lo personal, en el "Game End" event me gusta llamar al destroy event usando "event_perform(ev_destroy,0)", así no tengo que copiar y pegar todo lo que ponga en el Destroy.
En GMS2 se creó el "Clean Up" event para facilitar la destrucción de las Data Structures.

Ahora el rebote. En el evento de colisión del proyectil contra el enemigo:
[gml]//Sólo hace algo si el proyectil NO ha colisionado con ese enemigo.
if ds_list_find_index(objetivos_golpeados,other) = -1 { //La palabra "other" es el ID del objeto enemigo con el que el proyectil ha colisionado. Si NO lo encuentra en la DS LIST, la función regresa "-1".
other.hp -= 10//Aqui corres tu línea para causar daño al enemigo, yo usé esta de ejemplo con la variable "hp". Si tu enemigo no tiene la variable "hp" te dará bug.
ds_list_add(objetivos_golpeados,other) //Esto añade ese Enemigo a la Lista de enemigos golpeados.
var next_target = noone //Este será el ID del próximo enemigo a perseguir.
var min_distance = -1 //Esto es simplemente para determinar cuál es el enemigo más cercano.
var _list = objetivos_golpeados
with ( obj_enemy ) if ds_list_find_index(other.objetivos_golpeados,id) = -1 {
if min_distance = -1 { min_distance = point_distance(x,y,other.x,other.y); next_target = id }
else {
var my_distance = point_distance(x,y,other.x,other.y)
if my_distance < min_distance { min_distance = my_distance; next_target = id }
}
}
//Después del With, la variable "next_target" tendrá el ID del enemigo más cercano o "noone" si no encontró ninguno
if next_target != noone {
direction = point_direction(x,y,next_target.x,next_target.y) //Esto le cambia la dirección al proyectil para dirigirse ahora hacia el nuevo enemigo
}
}
[/gml]

He llamado al enemigo "obj_enemy", tú llámalo como le hayas puesto.

Ojo que al usar la función "with" te cambia el Scoping de las variables (no sé como se dice en español... alcance de las variables?) eso quiere decir que todas las variables de instance que uses, no se escriben/leen desde el proyectil, sino que se harán desde el enemigo, incluso la palabra "other" significa el enemigo fuera del With y el proyectil dentro del With.
Las variables locales que declaré con la palabra "var" adelante no se ven afectadas por el Scoping, así que las puede leer cualquier objeto tal como si fuesen variables globales.

Recuerda ponerle un tiempo de duración de vida al proyectil o algún contador de rebotes para que se destruya.

Saludos
53
Hola FernandoNavarro

Simplemente guarda los archivos como "slot1.dat", "slot2.dat" y "slot3.dat". Y dentro del archivo carga una String que contenga el nombre de la partida.
Como son solamente 3 slots, no veo la necesidad de crear algún Array o Data Structure, simplemente crea 3 variables diferentes y repite el código de lectura 3 veces.

Así cuando inicies el juego, primero le asignas al Slot un nombre por defecto, ejemplo "No hay partida guardada" y luego lees el nombre ingame para mostrar en el menu:
[gml]file1_name = "No hay juego guardado"
file2_name = "No hay juego guardado"
file3_name = "No hay juego guardado"
if file_exists("slot1.dat") {
//Abres el archivo con tu código
file1_name  = algún ini_read_string o file_text_read_string o como lo hayas hecho.
//Aquí cierras el archivo
}
//Repite lo mismo con los otros 2.
[/gml]

Si aún no creas ninguna partida en ese slot, entonces puedes dibujar la string por defecto "No hay partida guardada".

Cuando cargues un Slot, guarda el nombre del archivo en alguna variable global, para que puedas tener acceso al nombre del archivo desde cualquier objeto y desde cualquier room, así puedes guardar/cargar sobre el mismo slot en el que estás jugando sin alterar los otros 2.

Ejemplo, si cargas la tercera partida:
[gml]globalvar current_slot; current_slot = "slot3.dat"[/gml]

Así a futuro simplemente llamas a la variable "current_slot" para guardar tu partida

Saludos
54
Hola LastSparkle

Game Maker guarda las coordenadas que tenía el objeto en el Step Anterior.

Cuando cambias la "X" e "Y" de la plataforma, puedes encontrar cuántos pixeles se movió horizontalmente/verticalmente y aplicarlos al player.

Ejemplo, en el Evento que mueve tu plataforma:
[gml]//Primero calcula si el player está arriba tuyo:
var player_arriba  = false
if ( place_meeting(x,y-1,obj_player) ) { player_arriba = true } //Si la plataforma es atravezable, deberás hacer otro check.
//Ahora mueves la plataforma con el código que tengas
x = lengthdir_x bla bla
y = lengthdir_y lo mismo
//Acá la magia
var x_offset = x - xprevious //Esto calcula cuántos pixeles se movió la plataforma
var y_offset = y - yprevious
//Si el player estaba arriba de la plataforma, lo mueve también:
if ( player_arriba ) {
obj_player.x += x_offset
obj_player.y += y_offset
//Acá debes correr un código de colisión en el player para detectar si ha colisionado con algo, así evitas que el player se atasque.
//Según el resultado puedes regresar la plataforma a su posición anterior (x = xprevious) para no avanzar o botar al player de la plataforma o causarle daño al player o no mover el player, etc.
//Ejemplo, si el player colisiona, no mueve la plataforma:
var player_colisiona = false
with ( obj_player ) { if ( place_meeting(x,y,obj_wall) ) { player_colisiona = true } }
if ( player_colisiona = true ) { x = xprevious; y = yprevious; obj_player.x -= x_offset; obj_player.y += y_offset } //Esto regresa tanto a la plataforma como al player a la posición anterior.
}[/gml]

Yo he llamado al player "obj_player", pero tu le pones el nombre que usaste tú

Saludos
55
Hola towers

El Instance Id es un número único que Game Maker le asigna a cada objeto que creas en la room, comenzando desde 100000 hacia adelante (suma 1 por cada objeto nuevo que se crea en la room). Este número lo asigna tanto para objetos creados directamente desde el editor de rooms y los creados mediante las funciones instance_create (GMS 1.4) o instance_create_layer/instance_create_depth (GMS 2).

Si tu objeto se llama "obj_dron" y pusiste dos en la room, ellos comparten el mismo "object_index" que es "obj_dron", pero ambos tendrán un "instance ID" diferente,. El dron que se creó primero se llamará por ejemplo "100001" y el otro se llamará "100002".

Si usas el object index en un código
[gml]obj_dron.x = 100[/gml]
Todos los objetos "obj_dron" de la room se verán afectados.

Si en cambio usas el ID:
[gml]100001.x = 100[/gml]
Entonces sólo el objeto de ID 100001 se moverá al lugar indicado.

Mi recomendación es que NO crees los drones directamente en la room, sino que uses alguna de las funciones de "instance_create" para así poder conocer con facilidad el ID del objeto que estás creando y poder manipularlo.

Un ejemplo de lo que te comento sería esto, en el Create Event:
[gml]dron_primero = instance_create(100,100,obj_dron)
dron_segundo = instance_create(100,50,obj_dron)[/gml]
Ese código crea dos Drones en la room, y guarda sus ID en las variables "dron_primero" y "dron_segundo" (yo usé esos dos nombres para las variables como ejemplo pero tu las puedes llamar como quieras).

Ahora para manipularlos, simplemente usas la variable que creaste antes:
[gml]dron_primero.x = 100 //Esto cambia la "X" del primer dron solamente, sin afectar al segundo.
dron_segundo.x = 50 //Esto cambia la "X" del segundo dron.[/gml]

Y ya puedes manipular a cada dron por separado.

Si debes editar la posición de 30 drones, te recomiendo averiguar algo sobre "arrays" y "loops" (idealmente el "for" loop), porque crear 30 variables y repetir el código 30 veces para manipular los drones va a ser un caos.

Saludos
56
Hola towers

Crea una variable en el objeto quieto que controle el ángulo en el que se ubicará el segundo objeto respecto de él y una variable que controle la distancia, luego lo posicionas calculando el vector. Puedes usar las funciones trigonométricas "seno" o "coseno" o puedes usar las funciones integradas de GM "lengthdir".

Como ejemplo yo he creado la variable "angle"

En el create event:
[gml]angle = 0;[/gml]

Ahora en el Step de ese mismo objeto que está quieto, modificas la posición del segundo objeto.
[gml]var distancia = 80; //Este valor equivale al radio que forma la órbita del segundo objeto, usando el objeto quieto como centro
segundo.x = x + lengthdir_x(distancia,angle);
segundo.y = y + lengthdir_y(distancia,angle;
angle += 6 //la cantidad que quieras incrementar al ángulo por cada STEP[/gml]

Yo he llamado el objeto como "segundo", tú llámalo como quieras.
Si tienes 2 o más objetos llamados "segundo", deberás usar el Instance ID en lugar del Object Index para poder modificar la posición de sólo uno de los objetos.

Ojo: Favor notar que las distancias las calcula en función de los puntos de origen que le hayas asignado al Sprite.
Si los sprites tienen su origen en el centro, entonces lo verás bien, pero si tienen su origen en el x=0; y=0, entonces los verás un poco corridos hacia el lado, tendrás que sumarle algunos pixeles de offset para corregir la posición.

Saludos
57
Hola T789

Tienes razón, si quitas el estado "sólido" de la plataforma entonces todos los objetos de la room que interactúan con la plataforma se verán afectados.

Lo que se me ocurre es que hagas que las plataformas NO sean sólidas y uses la función "instance_place" para detectar la colisión. Esta función hace lo mismo que el "place_meeting", pero en vez de devolver "true/false", te devuelve el ID del objeto con el que colisionas (o "noone" si no colisionaste con ningún objeto).

Así podrás saber si colisionarás con una plataforma o no sin que la plataforma deba ser sólida.

[gml]var plataforma = instance_place(x,y+max(1,vspeed),obj_platform) //Esto guarda el ID de la plataforma con la que colisionas (o noone) en una variable local para no tener que repetir la condición después en el código. el objeto lo llamé "obj_platform", tú llámalo como lo hayas hecho tu.
var plataforma_debajo = false //Esto setea otra variable local llamada "plataforma_debajo" donde guardaremos "true" si debajo nuestro hay una plataforma o "false" si no lo hay. Por defecto su valor queda en false
if plataforma != noone { //Esto se ejecuta cuando el script detectó que debajo nuestro vamos a colisionar con una plataforma.
if !place_meeting(x,y,plataforma) { plataforma_debajo = true } //Esta línea hará la magia. En caso que abajo nuestro haya una plataforma y NO estés atravezándola, entonces la detecta como colisión. Si caes y atraviezas al menos 1 pixel de la plataforma, el código ya deja de detectarla como colisión, eso te permitirá atravezarlas al pulsar Abajo.
}

//Salto
if keyboard_check_pressed(vk_up) && (place_meeting(x,y+1,obj_block) || plataforma_debajo) { vspeed = -14 } //Si pulsas la flecha "arriba" y debajo tuyo hay un bloque sólido o debajo tuyo hay una plataforma, entonces te permite saltar.
//Aquí puse la tecla "arriba", nombré el objeto "obj_block" y asigné "vspeed" -14 como ejemplos, pero tú usa tus teclas, tu nombre de objeto y tu propio código de salto.

//Bajar las plataformas
if keyboard_check_pressed(vk_down) && !place_meeting(x,y+1,obj_block) && plataforma_debajo { y += 1; plataforma_debajo = false } //Si estás parado en una plataforma y apretas abajo, bajas 1 pixel para que el juego deje de detectar que estás sobre la plataforma y te deje atravezarla.

//Sino, primero colisiona con un bloque sólido
else if place_meeting(x,y+vspeed,obj_block) { while !place_meeting(x,y+sign(vspeed),obj_block) { y += sign(vspeed) }; vspeed = 0; } //Esta línea es un script de colisión precisa, hace lo mismo que el move_contact_solid pero funciona para colisionar contra objetos NO sólidos, en este caso le dije que chocara contra el "obj_block".
//Ahora colisiona con la plataforma (sólo si vas cayendo, si vas hacia arriba no hay ningún tipo de detección de colisión)
else if vspeed >= 0 { //sólo si caes
if plataforma_debajo { while !place_meeting(x,y+1,plataforma) { y += 1 }; vspeed = 0; } //Es el mismo código de colisión precisa.
}

//Caes por acción de la gravedad
if !place_meeting(x,y+1,obj_block) && !plataforma_debajo { gravity = 0.8 } //Si no hay nada debajo, caes por graverdad
else { gravity = 0 } //En cambio si había algo debajo, obviamente NO caes.[/gml]

Saludos
58
Hola T789

Lo ideal sería que usaras tu propio sistema de colisión.
Pero si prefieres usar el sistema integrado de game maker, lo que se me ocurre es que la plataforma sea sólida cuando el player esté por encima de la plataforma
y que sea NO sólida cuando el player está por debajo de la plataforma, además hacer la plataforma NO sólida cuando pulsas abajo.

Se me ocurre que en el STEP EVENT del objeto plataforma, añadas esto:
if object_exists(obj_player) { //Primero revisa si existe un objeto llamado "obj_player" en la room
if obj_player.bbox_bottom < bbox_top { solid = true } //Comprueba las coordenadas de la bounding box del player vs la plataforma. Si el player está arriba, la plataforma se hace sólida
else { solid = false } //Sino, la plataforma se hace NO sólida
}
if keyboard_check_pressed(vk_down) { //Cuando pulsas Abajo
solid = false //La plataforma ya no será sólida
}


Saludos
59
Hola Black_Cat

Concuerdo en que los objetos son mejores para el campo de juego porque le puedes pasar los atributos de la carta cuando la colocas en juego.
Y si clickeas la carta del campo, podrás leer fácilmente todos sus atributos.

Pero insisto que para la mano no sería recomendable a menos que crees un buen sistema de actualización de los objetos, variables y posiciones porque las cartas de la mano van cambiando constantemente, entonces tendrías que modificar los atributos constantemente.

No necesitas un ciclo for para leer los atributos de las cartas. Simplemente con el ID ya lo puedes sacar desde la DS GRID y leer el atributo que quieras.
Con otra persona de la comunidad estamos haciendo un RPG con más de 500 personajes, también guardamos los datos en una DS GRID y durante los combates no se nos lagea el juego cuando nos trae la info.

He asumido que tu grid tiene esta estructura:




0123...
ID1atributo1atributo2etc
ID2atributo1atributo2etc

Una vez conocido el ID de la carta, vas a buscar le ID en la grid
var id_carta = 0 //puse 0 como ejemplo, pero en realidad puedes usar el mouse check para traer el ID real de la carta que tengas
var yy = ds_grid_value_y(grid, 0, 0, 0, ds_grid_height(grid), id_carta) //Esta línea encuentra en que fila se encuentran los datos de tu carta en la GRID, leyendo los datos desde la columna 0
atributo1 = grid[# 1,yy]
atributo2 = grid[# 2,yy]
etc


Puedes actualizarlo cada vez que el mouse entra en las coordenadas de la mano e incluso evitar actualizarlo si mantienes el mouse sobre la misma carta para que no te vaya a leer la GRID en cada STEP.
Puedes usar dos variables "id_carta" e "id_carta_previo" para verificar si el mouse se puso sobre una carta diferente, para no correr el código en cada STEP.

Saludos
60
Hola FernandoNavarro

Debes guardar en alguna variable en algún objeto el ID del NPC con el que estás interactuando.

Cuando corras el Faceplayer, giras el NPC, no sé si usas un movimiento en 4 u 8 direcciones.

Yo uso este script:
angle = point_direction(npc.x,npc.y,player.x,player.y) div 90
Eso te da como resultado 4 valores posible, 0, 1, 2 y 3.
En función de ese valor hago el cambio de Sprite del NPC.

Saludos