前言
现在很多手游都有虚拟按钮–尤其是那些需要操作高的。那么我们也要紧跟时代步伐,开发一个虚拟按钮插件。
难点解释
1、首先绝对要先熟悉一下pixi。
2、要计算一下手指触摸拖动摇杆的角度–小学数学要过关,假如是小学连续留级十几年的话,会有点麻烦。
3、pixi有一些小bug,就是touch end会无缘无故由其他物体触发,代码里面已经有解决方案了。想知道原委就看看上一篇文章。
实际运行界面
核心代码
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String _js_version="7";
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta content="telephone=no,email=no" name="format-detection">
<meta name="full-screen" content="true"/>
<%--竖屏--%>
<%--<meta name="screen-orientation" content="portrait"/>--%>
<%--横屏--%>
<meta name="screen-orientation" content="landscape"/>
<meta name="x5-fullscreen" content="true"/>
<meta name="360-fullscreen" content="true"/>
<script type="text/javascript" src="../js/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="../js/jquery-migrate-1.2.1.min.js"></script>
<script src="../js/box2dWeb/Box2dWeb-2.1.a.3.min.js"></script>
<script src="../js/pixi.min.js"></script>
<script src="../js/pixi-spine.min.js"></script>
<style>
body{
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<div id="game" style=""></div>
<script>
var GameOptions={
width:800 //游戏屏幕的高度。
,height:600 //游戏屏幕的宽度。
,ground_y:400-65 //地面y坐标
,fps:10
,actorWidth:57*2*0.8
,actorHeight:61*2*0.8
//--hero的行走向量速度。
,hero_run_x_speed:15
,hero_run_y_speed:30 //hero跳跃时候向上的速度。
};
</script>
<script>
//--虚拟手柄控件。
function GameJoyPad(parent_container,_opts){
var me=this;
this.settings={
joy_pad_background:"assets/joystick/Backgrounds/Back_08.png"//摇杆的背景。
,joy_pad_joystick:"assets/joystick/Joystick/Joystick_08.png" //摇杆正体。
,joy_pad_x:100 //摇杆的坐标
,joy_pad_y:220 //摇杆的y坐标
//--注意,所有缩放的尺寸都是按照unitiy3d获得的这些摇杆素材来设置的,假如替换了texture,请重新设置缩放尺寸。
,joy_pad_background_scale:{
x:0.4
,y:0.4
}//摇杆背景需要缩放的比例,默认是x和y上面都是1
,joy_pad_joystick_scale:{
x:0.4
,y:0.4
}//摇杆主体需要缩放的比例,默认x、y都是1
,buttons:[
{
button_name:"shoot"
,normal_texture:"assets/joystick/Buttons/Button_08_Normal_Shoot.png"
,pressed_texture:"assets/joystick/Buttons/Button_08_Pressed_Shoot.png"
,x:300
,y:250
,scale:{x:0.4,y:0.4}
}
,{
button_name:"gun"
,normal_texture:"assets/joystick/Buttons/Button_08_Normal_Shoot_B.png"
,pressed_texture:"assets/joystick/Buttons/Button_08_Pressed_Shoot_B.png"
,x:400
,y:250
,scale:{x:0.4,y:0.4}
}
, {
button_name:"blank"
,normal_texture:"assets/joystick/Buttons/Button_08_Normal_Virgin.png"
,pressed_texture:"assets/joystick/Buttons/Button_08_Pressed_Virgin.png"
,x:500
,y:250
,scale:{x:0.4,y:0.4}
}
] //虚拟手柄的其他按钮。
//--摇杆摇动角度变换时候的回调函数。
,onJoyStickMove:function(now_stick_angle){
}
//--点击了控制按钮的回调事件。
,onButtonClick:function(event,button_name){
}
};
$.extend(this.settings,_opts);
//--基本赋值。
this.parent_container=parent_container;
this.joy_pad_container={};
this.joy_pad_background={};
this.joy_pad_joystick={};
this.joy_pad_radius=0;//这是背景大圈子的半径。
this.joy_pad_stickRadius=0;//这是摇杆小圈子的半径。
//--生成一个随机的joy container id。
this.joy_pad_container_id=new Date().getTime()+"_"+parseInt(Math.random()*1000);
//--好了,加载相关资源
me.__loadResources(function(){
me.__init_stick();
});
//--新建按钮。
for(var i=0;i< me.settings.buttons.length;i++){
var buttonItemInfo=me.settings.buttons[i];
me.__createButton(buttonItemInfo);
}
}
GameJoyPad.prototype.__loadResources=function(callback){
var me=this;
PIXI.loader.add('joy_pad_background',me.settings.joy_pad_background);
PIXI.loader.add('joy_pad_joystick',me.settings.joy_pad_joystick);
PIXI.loader.once('complete',function(){
if(callback){
callback();
}
});
PIXI.loader.load();
}
//--初始化摇杆。
GameJoyPad.prototype.__init_stick=function(){
var child=this;
var texture_bg=PIXI.Texture.fromImage(this.settings.joy_pad_background);
var texture_joystick=PIXI.Texture.fromImage(this.settings.joy_pad_joystick);
this.joy_pad_container=new PIXI.Container();
this.joy_pad_background=new PIXI.Sprite(texture_bg);
this.joy_pad_joystick=new PIXI.Sprite(texture_joystick);
this.joy_pad_background.scale=this.settings.joy_pad_background_scale;
this.joy_pad_joystick.scale=this.settings.joy_pad_joystick_scale;
this.joy_pad_background.anchor={x:0.5,y:0.5};
this.joy_pad_joystick.anchor={x:0.5,y:0.5};
this.joy_pad_container.anchor={x:0.5,y:0.5};
this.joy_pad_container.addChild(this.joy_pad_background);
this.joy_pad_container.addChild(this.joy_pad_joystick);
this.joy_pad_radius=this.joy_pad_container.width/2;
this.joy_pad_stickRadius=this.joy_pad_joystick.width/2;
window.joy_container=this.joy_pad_container;
this.joy_pad_container.position={
x:this.settings.joy_pad_x
,y:this.settings.joy_pad_y
};
this.parent_container.addChild(this.joy_pad_container);
this.joy_pad_container.random_id=this.joy_pad_container_id;
this.__init_stick_events();
}
GameJoyPad.prototype.__init_stick_events=function(){
var me=this;
this.joy_pad_container.interactive=true;
var _on_drag=false;
var _event_data={};
var _touch_event_id=0;
/******pixi bug1:当两个手指其中一个,譬如摇杆,另一个手指点击按钮,摇杆会接收到touch end事件。醉了。******/
function onDragStart(event){
//--注意,pc端的identifier是undefined。
_event_data = event.data;
var startPosition = _event_data.getLocalPosition(this.parent);
_touch_event_id=event.data.identifier;
_on_drag=true;
}
function onDragEnd(event){
if(_on_drag==false){
return;
}
if(_touch_event_id!=event.data.identifier){
return;
}
_on_drag=false;
window.end_event=event;
me.joy_pad_joystick.position={
x:0
,y:0
};
}
function onDragMove(event){
if(_touch_event_id!=event.data.identifier){
return;
}
if(_on_drag==false){
return;
}
var newPosition = _event_data.getLocalPosition(this.parent);
var _side_length_y=newPosition.y-me.settings.joy_pad_y;
var _side_length_x=newPosition.x-me.settings.joy_pad_x;
var _center_point={
x:0
,y:0
};//--中心点。
var _stick_angle=0; //当前摇杆的角度
if(_side_length_x==0&&_side_length_y==0){
return;
}
//--好了,现在判断执行计算的半径。
var _cal_radius=0;
if(_side_length_x*_side_length_x+_side_length_y*_side_length_y>=me.joy_pad_radius*me.joy_pad_radius){
_cal_radius=me.joy_pad_radius;
//--假如大于的话,那么就按照圆弧计算坐标。
}
else{
_cal_radius=me.joy_pad_radius-me.joy_pad_stickRadius;
}
if(_side_length_x==0){
if(_side_length_y>0){
_center_point={
x:0
,y:_side_length_y>me.joy_pad_radius?me.joy_pad_radius:_side_length_y
};
_stick_angle=270;//180度。
}
else{
_center_point={
x:0
,y:-(Math.abs(_side_length_y)>me.joy_pad_radius?me.joy_pad_radius:Math.abs(_side_length_y))
};
_stick_angle=90;//901度
}
me.joy_pad_joystick.position=_center_point;
me.settings.onJoyStickMove(_stick_angle);
return;
}
else if(_side_length_y==0){
if(_side_length_x>0){
_center_point={
x:(Math.abs(_side_length_x)>me.joy_pad_radius?me.joy_pad_radius:Math.abs(_side_length_x))
,y:0
};
_stick_angle=0;//0度
}
else{
_center_point={
x:-(Math.abs(_side_length_x)>me.joy_pad_radius?me.joy_pad_radius:Math.abs(_side_length_x))
,y:0
};
_stick_angle=180;//180度
}
me.joy_pad_joystick.position=_center_point;
me.settings.onJoyStickMove(_stick_angle);
return;
}
var _tan_val=Math.abs(_side_length_y/_side_length_x);
var _radian=Math.atan(_tan_val);
var _angle=_radian*180/Math.PI;
_stick_angle=_angle;
//--好了,计算现在摇杆的中心点主坐标了。
var _center_x= 0;
var _center_y=0;
if(_side_length_x*_side_length_x+_side_length_y*_side_length_y>=me.joy_pad_radius*me.joy_pad_radius){
_center_x= me.joy_pad_radius*Math.cos(_radian);
_center_y=me.joy_pad_radius*Math.sin(_radian);
}
else{
_center_x= Math.abs(_side_length_x)>me.joy_pad_radius?me.joy_pad_radius:Math.abs(_side_length_x);
_center_y=Math.abs(_side_length_y)>me.joy_pad_radius?me.joy_pad_radius:Math.abs(_side_length_y);
}
if(_side_length_y<0){
_center_y=-Math.abs(_center_y);
}
if(_side_length_x<0){
_center_x=-Math.abs(_center_x);
}
if(_side_length_x>0&&_side_length_y<0){
//--锐角。
}
else if(_side_length_x<0&&_side_length_y<0){
//--好了,钝角。
_stick_angle=180-_stick_angle;
}
else if(_side_length_x<0&&_side_length_y>0){
_stick_angle=_stick_angle+180;
}
else if(_side_length_x>0&&_side_length_y>0){
_stick_angle=360-_stick_angle;
}
_center_point={
x:_center_x
,y:_center_y
};
me.joy_pad_joystick.position=_center_point;
me.settings.onJoyStickMove(_stick_angle);
};
// events for drag start
this.joy_pad_container.on('mousedown', onDragStart)
.on('touchstart', onDragStart)
// events for drag end
.on('mouseup', onDragEnd)
.on('mouseupoutside', onDragEnd)
.on('touchend', onDragEnd)
.on('touchendoutside', onDragEnd)
// events for drag move
.on('mousemove', onDragMove)
.on('touchmove', onDragMove);
}
GameJoyPad.prototype.__createButton=function(buttonItemInfo){
var me=this;
var textureButton = PIXI.Texture.fromImage(buttonItemInfo.normal_texture);
var textureButtonDown = PIXI.Texture.fromImage(buttonItemInfo.pressed_texture);
var textureButtonOver = PIXI.Texture.fromImage(buttonItemInfo.normal_texture);
var button = new PIXI.Sprite(textureButton);
button.buttonMode = true;
button.anchor.set(0.5);
button.position.x = buttonItemInfo.x;
button.position.y = buttonItemInfo.y;
button.interactive = true;
if(buttonItemInfo.scale){
button.scale=buttonItemInfo.scale;
}
var _event_data_identifier=0;
function onButtonDown(event)
{
this.isdown = true;
this.texture = textureButtonDown;
this.alpha = 1;
_event_data_identifier=event.data.identifier;
}
function onButtonUp(event)
{
if(_event_data_identifier!=event.data.identifier){
return;
}
this.isdown = false;
if (this.isOver)
{
this.texture = textureButtonOver;
}
else
{
this.texture = textureButton;
}
}
function onButtonOver()
{
this.isOver = true;
if (this.isdown)
{
return;
}
this.texture = textureButtonOver;
}
function onButtonOut()
{
this.isOver = false;
if (this.isdown)
{
return;
}
this.texture = textureButton;
}
button.on('mousedown', onButtonDown)
.on('touchstart', onButtonDown)
// set the mouseup and touchend callback...
.on('mouseup', onButtonUp)
.on('touchend', onButtonUp)
.on('mouseupoutside', onButtonUp)
.on('touchendoutside', onButtonUp)
// set the mouseover callback...
.on('mouseover', onButtonOver)
// set the mouseout callback...
.on('mouseout', onButtonOut);
// you can also listen to click and tap events :
//.on('click', noop)
var noop = function (_event) {
me.settings.onButtonClick(_event,buttonItemInfo.button_name);
};
button.tap = noop;
button.click = noop;
this.parent_container.addChild(button);
return button;
}
</script>
<script>
var game_renderer = PIXI.autoDetectRenderer(GameOptions.width, GameOptions.height,{backgroundColor : 0x1099bb});
var game_stage = new PIXI.Container(0x66FF99);
$("#game").append(game_renderer.view);
var _joy_pad=new GameJoyPad(game_stage,{
//--摇杆摇动角度变换时候的回调函数。
onJoyStickMove:function(now_stick_angle){
_showMsg("摇杆角度为:"+now_stick_angle);
}
//--点击了控制按钮的回调事件。
,onButtonClick:function(event,button_name){
_showMsg("点击的按钮名称是:"+button_name);
}
});
var style = {
font : 'bold italic 20px Arial',
fill : '#F7EDCA',
stroke : '#4a1850',
// strokeThickness : 5,
// dropShadow : true,
// dropShadowColor : '#000000',
// dropShadowAngle : Math.PI / 6,
// dropShadowDistance : 6,
wordWrap : true,
wordWrapWidth : 300
};
var richText = new PIXI.Text('Rich text with a lot of options and across multiple lines',style);
richText.x = 0;
richText.y = 0;
game_stage.addChild(richText);
function game_animate(){
requestAnimationFrame(game_animate);
game_renderer.render(game_stage);
}
requestAnimationFrame(game_animate);
</script>
<script>
function _debug(msg){
var _str=richText.text;
richText.text=_str+"\n"+msg;
}
function _showMsg(msg){
richText.text=msg;
}
</script>
</body>
</html>