1.- CARACTERISTICAS DEL ESCENARIO
- Tamaño: 550px de ancho por 400 px de alto (pueden usar cualquier tamaño)
- Velocidad: 30 fps (pueden usar cualquier velocidad, pero si influirá en el resultado final)
- 3 capas: en la primera el AS3, la segunda para un texto dinámico con nombre de instancia
puntos, la ultima para el fondo, en mi caso solo use un borde color negro de 3 pts. de ancho
- 3 movieclips: despues de crearlos, los exportamos y vinculamos para poder usarlos desde la biblioteca, y los borramos del escenario.
el primero seria un objeto (yo use un circulo, pero pueden usar cualquier otra forma) de 50 px con una flechita en el borde superior, alineado al centro del escenario, exportamos como MiObjeto
el segundo seria un movieclip (yo use un circulo) de 20 px alineado al centro del escenario (pueden usar cualquier otra forma o imagen png, pero su tamaño afecta su gravedad ya que usaremos su tamaño como referencia de su peso) , exportamos como MiClip
el ultimo una flecha de 52 de ancho por 30 de largo aprox. alineada al centro a la izquierda como en la imagen, exportamos como FlechaPower
2.- OK, una vez realizado estos pasos, vamos a con el AS3, ya tengo preparado un código explicado y se los voy a pegar tal cual, posteriormente les comparto el archivo fla para que lo puedan evaluar y mejorar.
Si alguien tiene alguna duda pregunten, igual no lo se todo y pienso que con la retroalimentacion nos podemos ayudar entre todos y a mejorar lo que hacemos, un saludo afectuoso!!
Código ActionScript :
//importamos un tween y easing que utilizaremos mas adelante import fl.transitions.Tween; import fl.transitions.TweenEvent; import fl.transitions.easing.*; /* Creamos 3 movieclips para usar desde la biblioteca asignandole una clase y nombre de instancia: */ //circulo de 50px convertido a movie clip, punto registro al centro, con una flechita en borde superior var miobjeto:MiObjeto = new MiObjeto(); //circulo de 20px convertido a movie clip, punto registro al centro var miclip:MiClip = new MiClip(); //flecha de 53px ancho por 30px alto convertido a movie clip, punto registro al centro a la izquierda var powerDir:FlechaPower = new FlechaPower(); //variable tipo array con la que se creara un nuevo objeto var objetos:Array = new Array(); //y otra variable "i" con la que le daremos un valor inicial de 0 al array var i:int=0; //variables con las que almacenaremos el ancho y alto de nuestro escenario var stageW:Number = stage.stageWidth; var stageH:Number = stage.stageHeight; //variables solo declaradas (mas adelante le daremos un valor) var peso:Number;//peso del clip a lanzar var fuerza:Number;//fuerza con que se lanza al clip var angulo:Number;//angulo de disparo var angulo_rad:Number;//convertir angulos a radianes var vx:Number;//velocidad x del clip var vy:Number;//velocidad y del clip var rumboX:Number;//rumbo x de la flechaPower var rumboY:Number;//rumbo y de la flechaPower var randomObjetoX:Number;//posicion x objeto var randomObjetoY:Number;//posicion y objeto //variable que vale la mitad de ancho y alto en pixeles del clip var radioW:Number = miclip.width/2; var radioH:Number = miclip.height/2; //variable para el valor de la friccion constante que sufre la aceleracion var friccion:Number = .99; //valor de la gravedad terrestre (redondeado) var gravedad:Number=10; var randomNum:Number;//numero random para rotacion de objeto var randomVel:Number;//velocidad de desplazamiento random del objeto var randomTime:Number;//tiempo random en milseg que dura el objeto en escena var anguloObj:Number;//angulo de desplazamiento del objeto var anguloObj_rad:Number;//conversion a radianes del angulo de desplazamiento del objeto var vxObj:Number;//velocidad exponencial x del objeto var vyObj:Number;//velocidad exponencial y del objeto var tiempo:Timer;//clase timer para la duracion de desplazamiento del objeto var objetoTw:Tween;//clase tween para ease in u out del objeto var miClip:MovieClip = MovieClip(miclip);//clase movieclip para colision compleja de clip var miObjeto:MovieClip = MovieClip(miobjeto);//clase movieclip para colision compleja de objeto var miclipRect:Rectangle;//clase rectangle para colision de clip var miclipOffset:Matrix;//clase matrix para colision de clip var miclipBmpData:BitmapData;//clase bitmapdata para colision de clip var objetoRect:Rectangle;//clase rectangle para colision de objeto var objetoOffset:Matrix;//clase matrix para colision de objeto var miobjetoBmpData:BitmapData;//clase bitmapdata para colision de objeto var objetoLoc:Point;//punto de colision de objeto var miclipLoc:Point;//punto de colision de clip //marcador inicial en 0 dentro del texto dinamico puntos var score:int = 0;//variable score que inicialmente empieza con 0 de valor y sirve para los puntos puntos.text = ""+score;//sumamos la variable score al campo de texto dinamico puntos addChild(powerDir);//agregamos al escenario flechaPower addChild(miclip);//agregamos al escenario clip miclip.x = stageW / 2;//acomodamos miclip en medio del escenario miclip.y = stageH - miclip.height / 2;//acomodamos miclip en el borde de la parte baja powerDir.scaleX = powerDir.scaleY = .7;//bajamos la escala de la flechaPower //funcion que mueve angulo de la flecha segun la posicion del raton function flechaPos(e:Event){ powerDir.x = miclip.x;//centramos la flecha en el centro del clip powerDir.y = miclip.y;//centramos la flecha en el centro del clip powerDir.visible = true;//la flecha es visible rumboX = mouseX - powerDir.x;//valor variable rumboX segun mouseX respecto a posicion flecha x rumboY = mouseY - powerDir.y;//valor variable rumboY segun mouseY respecto a posicion flecha y //traducimos el rumbo xy de radianes a grados para rotar flecha, redondeamos la ecuación powerDir.rotation = int(Math.atan2(rumboY,rumboX) * 180 / Math.PI); angulo = -powerDir.rotation;//usamos rotacion de flecha para dar valor a angulo de trayecto de clip angulo_rad = angulo*Math.PI/180;//convertimos angulos a radianes para dar rumbo xy a trayecto de clip } //funcion se dispara al presionar boton de raton izq, la flecha da angulo y rumbo segun su ancho function chekPower(e:Event){ peso = ((miclip.width + miclip.height) / 2) / 100;//usamos las dimensiones de miClip para dar el peso //incrementamos exponencialmente la escala de la flechaPower mientras el boton izq del raton este DOWN powerDir.scaleX += .05; //usamos la escala de la flechaPower para darle un valor a la fuerza (igual pueden usar otro valor) fuerza = (powerDir.scaleX * powerDir.scaleX) * 15; //usamos formula newton para calcular velocidad y rumbo xy a la q viajara clip segun peso y fuerza vx = (fuerza/peso)*Math.cos(angulo_rad);// velocidad = fuerza/peso vy = (-fuerza/peso)*Math.sin(angulo_rad);//invertimos Y ya que plano cartesiano no corresponde al escenario //si la escala de la flecha excede cierto tamaño, la regresamos al tamaño que estamos usando if(powerDir.scaleX >= 2){ powerDir.scaleX = .7; } } //funcion que se ejecuta continuamente cuando soltamos el boton izquierdo del mouse //con esta funcion haremos el efecto de parabola en miclip function moverMc(e:Event){ //vamos a redondear todas las operaciones a 2 decimales: Math.round(()*100)/100 //lo haremos asi por que usaremos estas cifras para detener algunos eventos incluyendo este powerDir.visible = false;//la flecha es NO visible //esta operacion nos da la gravedad, obviamente la gravedad solo afecta la posicion Y de miClip vy = Math.round((vy + gravedad)*100)/100; //como en la funcion flechaPos ya dijimos q vx y vy equivalen al seno y coseno del angulo de rotacion de //flechaPower, entonces solo redondeamos el valor ya establecido vy = Math.round((vy)*100)/100; vx = Math.round((vx)*100)/100; //ahora usaremos la friccion para darle un efecto mas realista tanto a la parabola como al rebote de miClip //tambien podemos multiplicar el valor por algun decimal no mayor a 1 para afectar el rebote/friccion de miClip vx = Math.round((vx*friccion*.8)*100)/100; vy = Math.round((vy*friccion*.8)*100)/100; //la posicion x Y de miClip la establecemos asi, para evitar que el movimiento se vea interrumpido por //la repeticion del evento, aparte si no le damos un valor de posicion x y a miClip este no se movera miclip.x = miclip.x + vx; miclip.y = miclip.y + vy; //detenemos este evento, el valor de 4.4 puede variar si cambiamos cualquier valor de gravedad o friccion if(vy == 4.4){ miclip.removeEventListener(Event.ENTER_FRAME,moverMc);//miClip deja de moverse stage.addEventListener(MouseEvent.MOUSE_UP,upBtn);//add evento Mouse UP stage.addEventListener(MouseEvent.MOUSE_DOWN,downBtn);//add evento Mouse DOWN addEventListener(Event.ENTER_FRAME,flechaPos);//add evento flechaPos powerDir.scaleX = .7;//regresamos la flecha a la escala inicial } //con este trace podemos saber los valores de Y que se repiten cuando deja de botar el miClip //asi usamos este valor en un if == para poder detener el evento trace("velocidadY:"+vy); //con esta serie de if elseif registramos los bordes del escenario donde rebotara miClip if(miclip.y+radioH > stageH){ miclip.y=stageH-radioH; vy=vy*-1; } else if(miclip.y-radioH<0){ miclip.y=radioH; vy=vy*-1; } if(miclip.x+radioW>stageW){ miclip.x=stageW-radioW; vx=vx*-1; } else if(miclip.x-radioW<0){ miclip.x=radioW; vx=vx*-1; } cheka();//funcion que evalua constantemente si miClip y el objeto chocan mientras miClip se mueve } //funciones que usaremos para dar un valor mínimo y máximo para conseguir varios valores aleatorios //posición X aleatoria del objeto function minmaxObjetoX( min:Number, max:Number ):Number { return int(min + (max - min) * Math.random()); } //posición Y aleatoria del objeto function minmaxObjetoY( min:Number, max:Number ):Number { return int(min + (max - min) * Math.random()); } //rotación aleatoria del objeto function minmaxNum( min:Number, max:Number ):Number { return int(min + (max - min) * Math.random()); } //velocidad aleatoria del objeto function minmaxVel( min:Number, max:Number ):Number { return int(min + (max - min) * Math.random()); } //tiempo aleatorio que dura el objeto en escena entre uno y otro objeto function minmaxTime( min:Number, max:Number ):Number { return int(min + (max - min) * Math.random()); } //con esta función haremos que el objeto aparezca en un lugar XY aleatorio con una velocidad aleatoria //y con una trayectoria, por supuesto también aleatoria cada determinado //tiempo también aleatorio, //igual podrían determinar los valores como fijos, yo preferí hacerlo así clonar(); function clonar():void{ randomNum = minmaxNum(-179,179);//el valor de rotación rad de un objeto es de -179 a 179 no de 0 a 360 randomVel = minmaxNum(10,20);//el valor de velocidad tiene que ver con los pixeles, de 10 a 20 px en mi caso randomTime = minmaxNum(2000,5000);// la velocidad es en milisegundos, 1000 mlseg equivalen a 1 segundo anguloObj = -randomNum;//invertimos valor de angulo de rotación para que corresponda a la del objeto anguloObj_rad = anguloObj*Math.PI/180;//convertimos ángulos a radianes para trayecto de objeto //usamos los radianes para darle rumbo y velocidad x y al objeto vxObj = randomVel * Math.cos(anguloObj_rad); vyObj = randomVel * Math.sin(anguloObj_rad); addChild(miobjeto);//agregamos el objeto al escenario objetos.push(miobjeto);//disparamos el array para que haga un objeto nuevo //damos valor a las variables de posicion x y del objeto, de tal manera que el objeto no quede fuera //de los bordes del escenario randomObjetoX = minmaxObjetoX(miobjeto.width,stageW - miobjeto.width); randomObjetoY = minmaxObjetoY(miobjeto.height,stageH - miobjeto.height); //usamos la variable de tipo timer para crear un tiempo con el que cambiaremos de manera aleatoria //la posicion y trayectoria x y del objeto cada cierto tiempo, tambien aleatorio tiempo = new Timer(randomTime, 1);//el timer tiene tiempo aleatorio, solo se usa una vez tiempo.start();//empezamos el timer tiempo.addEventListener(TimerEvent.TIMER_COMPLETE,cambiar);//cuando timer termina cambiar //con este for creamos una cantidad infinita de objetos ya q i siempre es menor a longitud de los objetos for(i=0; i<objetos.length; ++i){ objetos[i].x = randomObjetoX;//la posicion x del objeto es aleatoria objetos[i].y = randomObjetoY;//la posicion y del objeto es aleatoria //tween IN de alpha cada que se cree un objeto nuevo objetoTw = new Tween(objetos[i], "alpha", Strong.easeInOut, 0, 1, 1, true); addEventListener(Event.ENTER_FRAME,siempre); } } //funcion que continuamente cheka que el objeto no pase de los bordes del escenario, y cuando lo hace //rebota en trayectoria proporcionalmente inversa function siempre (e:Event):void{ //la trayectoria x y del objeto corresponde a su angulo de rotacion miobjeto.x = miobjeto.x + vxObj; miobjeto.y = miobjeto.y + vyObj; //el objeto tambien rota con respecto a su trayectoria miobjeto.rotation = int(Math.atan2(vxObj,-vyObj) * 180 / Math.PI); //con esta serie de if elseif registramos los bordes del escenario donde rebotara el objeto if(miobjeto.y+(miobjeto.height/2) > stageH){ miobjeto.y=stageH-(miobjeto.height/2); vyObj=vyObj*-1; } else if(miobjeto.y-(miobjeto.height/2)<0){ miobjeto.y=miobjeto.height/2; vyObj=vyObj*-1; } if(miobjeto.x+(miobjeto.width/2)>stageW){ miobjeto.x=stageW-(miobjeto.width/2); vxObj=vxObj*-1; } else if(miobjeto.x-(miobjeto.width/2)<0){ miobjeto.x=miobjeto.width/2; vxObj=vxObj*-1; } } // funcion de tipo timer que se ejecuta cuando se termina el timer, dispara un easeOUT del objeto function cambiar (e:TimerEvent):void{ objetoTw = new Tween(miobjeto, "alpha", Strong.easeInOut, 1, 0, 1, true); //cuando se termine el easeOUT se ejecuta vadenuez objetoTw.addEventListener(TweenEvent.MOTION_FINISH,vadenuez); } //funcion de tipo tween con la que detenemos el listener del timer y clonamos un nuevo objeto function vadenuez(e:TweenEvent):void{ tiempo.reset();//damos reset al timer tiempo.removeEventListener(TimerEvent.TIMER_COMPLETE,cambiar);//borramos listener de timer removeEventListener(Event.ENTER_FRAME,siempre);//borramos evento siempre clonar();//funcion clonar } //funcion con la que registraremos las colisiones entre miClip y el objeto function cheka ():void{ puntos.text = ""+score;//el marcador siempre se actualizara con el valor del score miclipRect = miClip.getBounds(this);//obtener limites del rectangulo delimitado de miClip miclipOffset = miclip.transform.matrix;//transformar la matriz de miClip miclipOffset.tx = miclip.x - miclipRect.x;//offset de transfX de miClip menos su rect delimitador miclipOffset.ty = miclip.y - miclipRect.y; //offset de transfY de miClip menos su rect delimitador //la colision compleja requiere parametros de bitmapdata, con esto pasamos del cuadro delimitador //a los bordes de miClip miclipBmpData = new BitmapData(miclipRect.width, miclipRect.height, true, 0); miclipBmpData.draw(miclip, miclipOffset); //hacemos igual para el objeto objetoRect = miobjeto.getBounds(this); miobjetoBmpData = new BitmapData(objetoRect.width, objetoRect.height, true, 0); objetoOffset = miobjeto.transform.matrix; objetoOffset.tx = miobjeto.x - objetoRect.x; objetoOffset.ty = miobjeto.y - objetoRect.y; miobjetoBmpData.draw(miobjeto, objetoOffset); //con estas variables registramos la colision entre dos puntos objetoLoc = new Point(objetoRect.x, objetoRect.y); miclipLoc = new Point(miclipRect.x, miclipRect.y); //usamos if para chekar las condiciones de la colision de la siguiente manera if(miobjetoBmpData.hitTest(objetoLoc,255,miclipBmpData,miclipLoc,255)){ //use un filtro glow en ambos clips, si dejamos vacios los parametros nos da valores default miclip.filters = [new GlowFilter()]; miobjeto.filters = [new GlowFilter()]; miobjeto.scaleX = miobjeto.scaleY = 1.2;//ademas escalamos el objeto para apreciar mejor la colision score = fuerza+score; } //mientras no se produzca colision alguna, vaciamos los clips de los filtros usados y devolvemos al objeto //su escala original else { miclip.filters = []; miobjeto.filters = []; miobjeto.scaleX = miobjeto.scaleY = 1; } miclipBmpData.dispose();//deshechamos bitmap para que no ocupe memoria de mas miobjetoBmpData.dispose();//deshechamos bitmap para que no ocupe memoria de mas } //agregamos funciones de boton al escenario o stage //funcion del boton izquierdo del mouse cuando esta DOWN function downBtn(e:Event){ //mientras el botón izq. del mouse este abajo, se va a ejecutar el evento chekPower powerDir.addEventListener(Event.ENTER_FRAME,chekPower); } //funcion del boton izquierdo del mouse cuando esta UP function upBtn(e:Event){ /* al soltar el botón izq. del mouse eliminaremos el evento que cheka la posición de la flecha así como el evento que se ejecuto al estar el botón izq. del mouse abajo que cheka la fuerza, ademas eliminamos los eventos de los botones mouse UP o DOWN para que no interfieran durante la trayectoria de miClip, solo quedara entonces agregar el listener que permita el movimiento de miClip */ removeEventListener(Event.ENTER_FRAME,flechaPos); powerDir.removeEventListener(Event.ENTER_FRAME,chekPower); stage.removeEventListener(MouseEvent.MOUSE_DOWN,downBtn); stage.removeEventListener(MouseEvent.MOUSE_UP,upBtn); miclip.addEventListener(Event.ENTER_FRAME,moverMc); } //por ultimo los listeners que se van a ejecutar al inicar la pelicula addEventListener(Event.ENTER_FRAME,flechaPos); stage.addEventListener(MouseEvent.MOUSE_UP,upBtn); stage.addEventListener(MouseEvent.MOUSE_DOWN,downBtn);
Archivo Flash (CS4)
Archivo FLA
Ya al final pueden cambiar los objetos por imágenes png y verán el resultado de la colisión compleja, lo único que falto agregar seria que la piedra también rotara según donde rebote y su fuerza.