更新代码

This commit is contained in:
caochao
2019-05-15 10:14:31 +08:00
parent 4d26472d87
commit 0326e9e002
40 changed files with 7848 additions and 2785 deletions

322
README.md
View File

@@ -3,130 +3,232 @@
使用说明
1.ListView, 循环滚动列表固定尺寸item, 屏幕可见范围外item会回收等待下次复用。支持横向竖向多行多列。
1.ListView, 循环滚动列表固定尺寸item, 屏幕可见范围外item会回收等待下次复用。支持横向竖向多行多列。支持追加删除数据。支持上右下左padding, 支持设置item锚点。
* 初始化,传入item模板节点(cc.Node),设置各种回调函数
* 初始化,传入各种参数设置item class
```
@property(cc.ScrollView)
scrollview: cc.ScrollView = null;
@property(cc.Node)
mask: cc.Node = null;
scrollView: cc.Node = null;
@property(cc.Node)
content: cc.Node = null;
@property(cc.Node)
item_tpl:cc.Node = null;
private list:ListView.ListView;
private _listView:ListView;
on_show(...params)
{
this.list = new ListView.ListView({
scrollview:this.scrollview,
mask:this.mask,
content:this.content,
item_tpl:this.item_tpl,
cb_host:this,
item_setter:this.list_item_setter,
select_cb:this.list_item_onselect,
recycle_cb:this.list_item_onrecycle,
const maskNode = this.scrollView.getChildByName("mask");
const content = maskNode.getChildByName("content");
const item_tpl = content.getChildByName("item");
const scrollview = this.scrollView.getComponent(cc.ScrollView);
const mask = maskNode.getComponent(cc.Mask);
this._listView = new ListView({
scrollview,
mask,
content,
item_tpl,
item_class:UnlockNewSongItem,
column:1,
gap_y:10,
direction:ListView.ListViewDir.Vertical,
gap_y:8,
item_anchorX:0.5,
item_anchorY:0.5,
direction:ListViewDir.Vertical,
});
this.list.set_data(Consts.AllStages);
this._listView.set_data(this._songs);
}
```
* 设置item回调函数
* 实现列表item回调方法
```
list_item_setter(item:cc.Node, desc:Consts.StageDesc, index:number):void
{
const isOpen = appdata.getStageOpenState(desc.stage, desc.unlockcond, desc.total);
const isPassed = appdata.isStagePassed(desc.stage, desc.total);
const cond = item.getChildByName("cond");
const txt_cond = cond.getChildByName("txt_cond");
const txt_progress = item.getChildByName("txt_progress");
const btn_share = item.getChildByName("btn_share");
const img_star = item.getChildByName("img_star");
const gold_star = img_star.getChildByName("gold_star");
//省略
}
```
2.ScrollView, 循环滚动列表支持不定尺寸的item, 屏幕可见范围外item会回收等待下次复用。支持横向竖向, 但不支持多行多列。
* 初始化传入item模板节点(cc.Node)列表,设置各种回调函数
```
const templates:ScrollItemTemplate[] = [
{key:MsgType.ROUND_START.toString(), node:this.item_roundstart},
{key:MsgType.LEFT_REDPACK.toString(), node:this.item_leftpack},
{key:MsgType.RIGHT_REDPACK.toString(), node:this.item_rightpack},
{key:MsgType.GRAB_NOTIFY.toString(), node:this.item_grab},
{key:MsgType.ROUND_END.toString(), node:this.item_common},
{key:MsgType.ROUND_RESULT.toString(), node:this.item_roundresult},
{key:MsgType.LEFT_CHAT.toString(), node:this.item_leftchat},
{key:MsgType.RIGHT_CHAT.toString(), node:this.item_rightchat},
{key:MsgType.JOIN_ROOM_NOTIFY.toString(), node:this.item_common},
{key:MsgType.QUIT_ROOM_NOTIFY.toString(), node:this.item_common},
{key:MsgType.DISMISS_ROOM_NOTIFY.toString(), node:this.item_common},
{key:MsgType.ROUND_TIMEOUT_NOTICE.toString(), node:this.item_common},
];
this.scview = new ScrollView({
scrollview:this.scrollview,
mask:this.mask,
content:this.content,
item_templates:templates,
cb_host:this,
item_setter:this.item_setter,
recycle_cb:this.list_item_recyle,
gap_y:10,
auto_scrolling:true,
direction:ScrollDirection.Vertical,
});
```
* 设置item回调内部根据传入的key及data为对应item节点设置数据
```
item_setter(item:cc.Node, key:string, data:any, index:number):[number, number]
export class UnlockNewSongItem extends ListViewItem
{
const enum_key:number = parseInt(key);
switch(enum_key)
{
case MsgType.ROUND_START:
item.getComponent(cc.Label).string = format.sprintf("第%d轮开始", data);
return [item.width, item.height];
private _bg:cc.Sprite;
private _txtSongName:cc.Label;
private _txtAuthor:cc.Label;
private _listenView:TrialListenView;
onInit()
{
const node = this.node;
this._txtSongName = node.getChildByName("songName").getComponent(cc.Label);
this._txtAuthor = node.getChildByName("author").getComponent(cc.Label);
this._listenView = new TrialListenView();
this._listenView.addTo(node, 223, -151);
}
onUnInit()
{
this._listenView.destroy();
this._listenView = null;
}
onSetData(songId:number, index:number)
{
const songCfg = appData.songLibrary.get(songId);
if(!songCfg) return;
//songname
this._txtSongName.string = songCfg.name;
this._txtAuthor.string = songCfg.author;
this._likeView.setVisible(true);
this._likeView.setLike(localData.songInfo.isFavoriteSong(songId));
//试听
if(songCfg.hasMp3) {
const listenState = Mp3Player.getInst().getListenState(songId);
this._listenView.setState(listenState || SongListenState.Idle);
}
else {
this._listenView.setState(SongListenState.None);
}
}
onRecycle(songId:number)
{
this._listenView.setState(SongListenState.None);
}
onTouchEnd(touchPos:cc.Vec2, songId:number, index:number)
{
if(this._listenView.handleTouchEnd(touchPos, songId)) {
return;
}
if(this._likeView.handleTouchEnd(touchPos, songId, source)) {
return;
}
}
}
```
2.ScrollView, 循环滚动列表支持不定尺寸的item, 屏幕可见范围外item会回收等待下次复用。支持横向竖向, 但不支持多行多列。支持追加删除数据。支持上右下左padding, 支持设置item锚点。
case MsgType.LEFT_REDPACK:
case MsgType.RIGHT_REDPACK:
return this.set_pack_item(item, data);
case MsgType.ROUND_RESULT:
let node_names:cc.Node = item.getChildByName("names");
node_names.getComponent(cc.Label).string = data;
item.height = node_names.height - node_names.y;
return [item.width, item.height];
case MsgType.LEFT_CHAT:
case MsgType.RIGHT_CHAT:
return this.set_chat_item(item, data);
case MsgType.GRAB_NOTIFY:
return this.set_grab_item(item, data);
case MsgType.DISMISS_ROOM_NOTIFY:
case MsgType.ROUND_TIMEOUT_NOTICE:
case MsgType.JOIN_ROOM_NOTIFY:
case MsgType.QUIT_ROOM_NOTIFY:
case MsgType.ROUND_END:
item.getComponent(cc.Label).string = data as string;
return [item.width, item.height];
default:
return [0, 0];
}
* 初始化传入各种参数设置item class
```
@property(cc.Node)
scrollView:cc.Node = null;
private initSongsView()
{
const maskNode = this.scrollView.getChildByName("mask");
const content = maskNode.getChildByName("content");
const item_tpl = content.getChildByName("item");
const scrollview = this.scrollView.getComponent(cc.ScrollView);
const mask = maskNode.getComponent(cc.Mask);
const templates:ScrollItemTemplate[] = [
{key:PushViewItemType.PushSongItem, node:item_tpl, item_class:LatestSongPushItem},
];
this._songsView = new ScrollPage({
scrollview,
mask,
content,
item_templates:templates,
gap_x:-30,
padding_left:124,
padding_right:124,
padding_top:200,
direction:ScrollDirection.Horizontal,
cb_host:this,
on_turning:this.onTurning,
on_scrolling:this.onScrolling,
item_anchorX:0.5,
item_anchorY:0.5,
});
const itemDatas:ScrollItemData[] = this._songs.map(data => {
return {
key:PushViewItemType.PushSongItem,
data,
}
});
this._songsView.set_data(itemDatas);
}
```
* 实现列表item回调方法
```
export class LatestSongPushItem extends ScrollViewItem
{
private _defaultCover:cc.Node;
private _coverSprite:cc.Sprite;
private _coverSpriteFrame:cc.SpriteFrame;
private _listenView:TrialListenView;
onInit(key:PushViewItemType)
{
const node = this.node;
this._defaultCover = node.getChildByName("defaultCover");
this._coverSprite = node.getChildByName("cover").getComponent(cc.Sprite);
this._coverSpriteFrame = new cc.SpriteFrame();
this._coverSpriteFrame.name = "LatestSongPushItemSpriteFrame";
this._listenView = new TrialListenView({
prefabPath:"prefabs/misc/trialListen2",
downloadingTexure:"xinge_tanchuang_shiting_02",
listeningDbPath:"main/xinge_shiting_dh",
listeningDbArmature:"xinge_shiting_dh",
listeningDbAnim:"xinge_shiting_dh",
listeningDbPosX:0,
listeningDbPosY:0,
});
this._listenView.addTo(node, 89, -331);
}
onUnInit(key:PushViewItemType)
{
this._listenView.destroy();
this._listenView = null;
this._coverSpriteFrame.clearTexture();
// this._coverSpriteFrame.destroy();
this._coverSpriteFrame = null;
}
private _currSongId:number;
onSetData(key:PushViewItemType, songId:number, index:number, is_mesure:boolean):[number, number]
{
if(is_mesure) {
return [this.node.width, this.node.height];
}
const songCfg = appData.songLibrary.get(songId);
if(!songCfg) return [this.node.width, this.node.height];
this._defaultCover.active = !songCfg.cover;
this._coverSprite.node.active = !!songCfg.cover;
if(songCfg.cover) {
this._currSongId = songId;
ImageUtil.getInst().setExternalSpriteFrame(songCfg.cover, songId, (tag, tex) => {
if(tag == this._currSongId && cc.isValid(this.node) && cc.isValid(this._coverSpriteFrame)) {
this._coverSpriteFrame.setTexture(tex);
this._coverSprite.spriteFrame = this._coverSpriteFrame;
}
});
}
//试听
if(songCfg.hasMp3) {
const listenState = Mp3Player.getInst().getListenState(songId);
this._listenView.setState(listenState || SongListenState.Idle);
}
else {
this._listenView.setState(SongListenState.None);
}
return [this.node.width, this.node.height];
}
onRecycle(key:PushViewItemType, songId:number, is_mesure:boolean)
{
if(is_mesure)
{
return;
}
this._coverSprite.spriteFrame = null;
this._listenView.setState(SongListenState.None);
}
onTouchEnd(key:PushViewItemType, songId:number, touchPos:cc.Vec2, index:number)
{
this._listenView.handleTouchEnd(touchPos, songId);
}
}
```
* 追加数据, 传入key及item数据

View File

@@ -1,278 +1,279 @@
import {loader_mgr} from "../loader/loader_mgr"
import * as utils from "../util"
import * as Consts from "../../consts"
import * as wxapi from "../wxapi/wxstorage"
const MUSIC_PATH = "sounds/music/{0}";
const SOUND_PATH = "sounds/sound/{0}";
export class AudioPlayer
{
private static inst:AudioPlayer;
private clip_cache:Map<string, cc.AudioClip>;
private loading_map:Map<string, boolean>;
private curr_music:string;
private music_id:number;
private music_volume:number;
private music_mute:boolean;
private sound_ids:number[];
private sound_volume:number;
private sound_mute:boolean;
private constructor()
{
this.music_id = -1;
this.sound_ids = [];
this.clip_cache = new Map();
this.loading_map = new Map();
}
static getInst()
{
if(!this.inst)
{
this.inst = new AudioPlayer();
}
return this.inst;
}
init()
{
let sound_vol_str = wxapi.wxStorage.get(Consts.Game.SoundVol);
let sound_mute_str = wxapi.wxStorage.get(Consts.Game.SoundMute);
let music_vol_str = wxapi.wxStorage.get(Consts.Game.MusicVol);
let sound_vol = sound_vol_str ? parseFloat(sound_vol_str) : 1;
let music_vol = music_vol_str ? parseFloat(music_vol_str) : 1;
let mute_int = sound_mute_str ? parseInt(sound_mute_str) : 0;
let is_mute = mute_int == 1;
this.set_music_mute(is_mute);
this.set_music_volumn(music_vol);
this.set_sound_mute(is_mute);
this.set_sound_volumn(sound_vol);
}
flush()
{
wxapi.wxStorage.set(Consts.Game.SoundMute, this.sound_mute ? "1" : "0");
}
//同时只能播放一个
play_music(name:string)
{
if(this.music_id >= 0)
{
this.stop_music();
}
let path = utils.strfmt(MUSIC_PATH, name);
this.curr_music = name;
if(this.music_mute)
{
cc.info("music is mute");
return;
}
let clip = this.clip_cache.get(path);
if(clip)
{
this.play_clip(clip, this.music_volume, true, AudioType.Music);
}
else
{
let task:AudioPlayTask = {type:AudioType.Music, name:name, path:path, volume:this.music_volume, loop:true};
this.load_task(task);
}
}
stop_music()
{
if(this.music_id < 0)
{
cc.info("no music is playing");
return;
}
cc.audioEngine.stop(this.music_id);
this.music_id = -1;
}
get_music_mute()
{
return this.music_mute;
}
set_music_mute(is_mute:boolean)
{
this.music_mute = is_mute;
if(this.music_id < 0)
{
if(!is_mute && this.curr_music)
{
this.play_music(this.curr_music);
}
return;
}
if(is_mute)
{
cc.audioEngine.pause(this.music_id);
}
else
{
cc.audioEngine.resume(this.music_id);
}
}
//0~1
set_music_volumn(volume:number)
{
this.music_volume = volume;
if(this.music_id >= 0)
{
cc.audioEngine.setVolume(this.music_id, volume);
}
}
private load_task(task:AudioPlayTask)
{
let path = task.path;
if(this.loading_map.get(path))
{
return;
}
this.loading_map.set(path, true);
loader_mgr.get_inst().loadRawAsset(path, utils.gen_handler(this.on_clip_loaded, this, task));
}
private on_clip_loaded(task:AudioPlayTask, clip:cc.AudioClip)
{
this.clip_cache.set(task.path, clip);
if(task.type == AudioType.Music && task.name != this.curr_music)
{
return;
}
this.play_clip(clip, task.volume, task.loop, task.type, task.cb);
}
private play_clip(clip:cc.AudioClip, volume:number, loop:boolean, type:AudioType, cb?:utils.handler)
{
let aid = cc.audioEngine.play(clip, loop, volume);
if(type == AudioType.Music)
{
this.music_id = aid;
}
else if(type == AudioType.Sound)
{
this.sound_ids.push(aid);
cc.audioEngine.setFinishCallback(aid, () => {
this.on_sound_finished(aid);
cb && cb.exec();
});
}
}
private on_sound_finished(aid:number)
{
let idx = this.sound_ids.findIndex((id) => {
return id == aid;
});
if(idx != -1)
{
this.sound_ids.splice(idx, 1);
}
}
//可同时播放多个
play_sound(name:string, cb?:utils.handler)
{
if(this.sound_mute)
{
cc.info("sound is mute");
return;
}
let path = utils.strfmt(SOUND_PATH, name);
let clip = this.clip_cache.get(path);
if(clip)
{
this.play_clip(clip, this.sound_volume, false, AudioType.Sound, cb);
}
else
{
let task:AudioPlayTask = {type:AudioType.Sound, name:name, path:path, volume:this.sound_volume, loop:false, cb};
this.load_task(task);
}
}
get_sound_mute()
{
return this.sound_mute;
}
set_sound_mute(is_mute:boolean)
{
this.sound_mute = is_mute;
this.sound_ids.forEach((sid) => {
if(is_mute)
{
cc.audioEngine.pause(sid);
}
else
{
cc.audioEngine.resume(sid);
}
});
}
//0~1
set_sound_volumn(volume:number)
{
this.sound_volume = volume;
this.sound_ids.forEach((sid) => {
cc.audioEngine.setVolume(sid, volume);
});
}
stop_sound()
{
this.sound_ids.forEach((sid) => {
cc.audioEngine.stop(sid);
});
this.sound_ids.length = 0;
}
clear_cache()
{
this.clip_cache.forEach((clip, key) => {
loader_mgr.get_inst().release(clip);
});
this.clip_cache.clear();
this.loading_map.clear();
cc.audioEngine.uncacheAll();
}
}
enum AudioType
{
Music = 1,
Sound = 2,
}
interface AudioPlayTask
{
type:AudioType;
name:string;
path:string;
volume:number;
loop:boolean;
cb?:utils.handler;
}
export const AUDIO_CONFIG = {
Audio_Btn:"audio_btn",
Audio_Hint:"audio_hint",
Audio_Vitory:"audio_victory",
Audio_Coin:"audio_chest_open",
Audio_XuanZhen:"audio_xuanzhe",
Audio_Unlock:"unlock",
Audio_Bgm:"bgm",
import {loader_mgr} from "../loader/loader_mgr"
import * as utils from "../util"
import { LocalStorage } from "../misc/storage";
import { Consts } from "../../const";
const MUSIC_PATH = "sounds/music/{0}";
const SOUND_PATH = "sounds/sound/{0}";
export class AudioPlayer
{
private static inst:AudioPlayer;
private loading_map:Map<string, boolean>;
private curr_music:string;
private curr_volume:number;
private music_id:number;
private music_volume:number;
private music_mute:boolean;
private sound_ids:number[];
private sound_volume:number;
private sound_mute:boolean;
private storage:LocalStorage;
private constructor()
{
this.music_id = -1;
this.sound_ids = [];
this.loading_map = new Map();
this.storage = LocalStorage.getInst();
}
static getInst()
{
if(!this.inst)
{
this.inst = new AudioPlayer();
}
return this.inst;
}
init()
{
let sound_vol = this.storage.getFloat(Consts.SoundVol, 1);
let music_vol = this.storage.getFloat(Consts.MusicVol, 1);
let is_mute = this.storage.getBool(Consts.SoundMute);
this.set_music_mute(is_mute);
this.set_music_volumn(music_vol);
this.set_sound_mute(is_mute);
this.set_sound_volumn(sound_vol);
}
flush()
{
// this.storage.setBool(Consts.SoundMute, this.sound_mute);
}
//同时只能播放一个
play_music(name:string, pVolume:number = null)
{
if(this.music_id >= 0)
{
this.stop_music();
}
const path = utils.strfmt(MUSIC_PATH, name);
this.curr_music = name;
this.curr_volume = pVolume;
if(this.music_mute)
{
cc.log("music is mute");
return;
}
const volume = pVolume || this.music_volume;
const task:AudioPlayTask = {type:AudioType.Music, name, path, volume, loop:true};
this.load_task(task);
}
stop_music()
{
if(this.music_id < 0)
{
cc.log("no music is playing");
return;
}
cc.audioEngine.stop(this.music_id);
this.music_id = -1;
}
get_music_mute()
{
return this.music_mute;
}
set_music_mute(is_mute:boolean)
{
this.music_mute = is_mute;
if(this.music_id < 0)
{
if(!is_mute && this.curr_music)
{
const music_id = this.curr_music;
const volume = this.curr_volume;
this.curr_music = null;
this.curr_volume = null;
this.play_music(music_id, volume);
}
return;
}
if(is_mute)
{
cc.audioEngine.pause(this.music_id);
}
else
{
cc.audioEngine.resume(this.music_id);
}
}
//0~1
set_music_volumn(volume:number)
{
this.music_volume = volume;
if(this.music_id >= 0)
{
cc.audioEngine.setVolume(this.music_id, volume);
}
}
private load_task(task:AudioPlayTask)
{
const path = task.path;
if(this.loading_map.get(path))
{
return;
}
this.loading_map.set(path, true);
if(task.external)
{
loader_mgr.get_inst().loadExternalAsset(path, utils.gen_handler(this.on_clip_loaded, this, task));
}
else
{
loader_mgr.get_inst().loadRes(path, utils.gen_handler(this.on_clip_loaded, this, task));
}
}
private on_clip_loaded(task:AudioPlayTask, clip:cc.AudioClip)
{
this.loading_map.delete(task.path);
if(task.type == AudioType.Music && task.name != this.curr_music)
{
return;
}
this.play_clip(clip, task.volume, task.loop, task.type, task.cb);
}
private play_clip(clip:cc.AudioClip, volume:number, loop:boolean, type:AudioType, cb?:utils.handler)
{
let aid = cc.audioEngine.play(clip, loop, volume);
if(type == AudioType.Music)
{
this.music_id = aid;
}
else if(type == AudioType.Sound)
{
this.sound_ids.push(aid);
cc.audioEngine.setFinishCallback(aid, () => {
this.on_sound_finished(aid);
cb && cb.exec();
});
}
}
private on_sound_finished(aid:number)
{
let idx = this.sound_ids.findIndex((id) => {
return id == aid;
});
if(idx != -1)
{
this.sound_ids.splice(idx, 1);
}
}
//可同时播放多个
play_sound(name:string, loop = false, pVolume:number = null, cb?:utils.handler)
{
if(this.sound_mute)
{
cc.log("sound is mute");
return;
}
const path = utils.strfmt(SOUND_PATH, name);
const volume = pVolume || this.sound_volume;
const task:AudioPlayTask = {type:AudioType.Sound, name, path, volume, loop, cb};
this.load_task(task);
}
play_external_sound(path:string, loop = false, pVolume:number = null, cb?:utils.handler)
{
if(this.sound_mute)
{
cc.log("sound is mute");
return;
}
const volume = pVolume || this.sound_volume;
const task:AudioPlayTask = {type:AudioType.Sound, name:path, path, volume, loop, external:true, cb};
this.load_task(task);
}
get_sound_mute()
{
return this.sound_mute;
}
set_sound_mute(is_mute:boolean)
{
this.sound_mute = is_mute;
this.sound_ids.forEach((sid) => {
if(is_mute)
{
cc.audioEngine.pause(sid);
}
else
{
cc.audioEngine.resume(sid);
}
});
}
//0~1
set_sound_volumn(volume:number)
{
this.sound_volume = volume;
this.sound_ids.forEach((sid) => {
cc.audioEngine.setVolume(sid, volume);
});
}
stop_sound()
{
this.sound_ids.forEach((sid) => {
cc.audioEngine.stop(sid);
});
this.sound_ids.length = 0;
}
clear_cache()
{
this.stop_music();
this.loading_map.clear();
cc.audioEngine.uncacheAll();
}
}
enum AudioType
{
Music = 1,
Sound = 2,
}
interface AudioPlayTask
{
type:AudioType;
name:string;
path:string;
volume:number;
loop:boolean;
external?:boolean;
cb?:utils.handler;
}
export const AUDIO_CONFIG = {
SE_01:"SE_01",
SE_02:"SE_02",
SE_03:"SE_03",
}

View File

@@ -1,17 +1,40 @@
//item及父节点锚点都为(0,1)
export class LayoutUtil
{
static vertical_layout(index:number, item_width:number, item_height:number, column:number = 1, gap_x:number = 0, gap_y:number = 0):[number, number]
{
let x:number = (index % column) * (item_width + gap_x);
let y:number = -Math.floor(index / column) * (item_height + gap_y);
return [x, y];
}
static horizontal_layout(index:number, item_width:number, item_height:number, row:number = 1, gap_x:number = 0, gap_y:number = 0):[number, number]
{
let x:number = Math.floor(index / row) * (item_width + gap_x);
let y:number = -(index % row) * (item_height + gap_y);
return [x, y];
}
//item及父节点锚点都为(0,1)
export class LayoutUtil
{
static vertical_layout(index:number, item_width:number, item_height:number, column = 1, gap_x = 0, gap_y = 0, padding_left = 0, padding_top = 0):[number, number]
{
const x = (index % column) * (item_width + gap_x) + padding_left;
const y = -Math.floor(index / column) * (item_height + gap_y) - padding_top;
return [x, y];
}
static horizontal_layout(index:number, item_width:number, item_height:number, row = 1, gap_x = 0, gap_y = 0, padding_left = 0, padding_top = 0):[number, number]
{
const x = Math.floor(index / row) * (item_width + gap_x) + padding_left;
const y = -(index % row) * (item_height + gap_y) - padding_top;
return [x, y];
}
static set_pivot_smart(node:cc.Node, ax:number, ay:number, recursive = false)
{
const deltaAx = ax - node.anchorX;
const deltaAy = ay - node.anchorY;
node.anchorX = ax;
node.anchorY = ay;
//改变节点锚点,位置值相对锚点不变。需要调整节点至新位置才能保持一致视觉效果
const deltaX = deltaAx * node.width;
const deltaY = deltaAy * node.height;
node.x += deltaX;
node.y += deltaY;
node.children.forEach(child => {
//父节点锚点改变,子节点相对父节点位置值不变,需要调整子节点至新位置才能保持一致视觉效果
child.x -= deltaX;
child.y -= deltaY;
if(recursive) {
LayoutUtil.set_pivot_smart(child, ax, ay);
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
const {ccclass, property} = cc._decorator;
@ccclass
export class ListViewItem extends cc.Component
{
// constructor()
// {
// super();
// }
onInit()
{
// cc.log("item初如化");
}
onUnInit()
{
// cc.log("item析构");
}
onSetData(data:any, index:number)
{
// cc.log("item设置数据");
}
onSetSelect(is_select:boolean, index:number)
{
// cc.log("item选中状态改变");
}
onRecycle(data:any)
{
// cc.log("item被回收");
}
onSelected(data:any, index:number)
{
// cc.log("item被选中");
}
onTouchEnd(touchPos:cc.Vec2, data:any, index:number)
{
// cc.log("item非滑动状态下被点击");
}
onBecameInvisible()
{
// cc.log("item从父节点移除或不可见");
}
setLeftTop(left:number, top:number)
{
this.node.x = left + this.node.anchorX * this.node.width;
this.node.y = top - (1 - this.node.anchorY) * this.node.height;
}
}

View File

@@ -0,0 +1,115 @@
const {ccclass, property} = cc._decorator;
@ccclass
export class PageIndicator extends cc.Component {
@property({type: cc.SpriteFrame})
selectedSpriteFrame: cc.SpriteFrame = null;
@property({type: cc.SpriteFrame})
unSelectedSpriteFrame: cc.SpriteFrame = null;
@property
spacing = 0;
@property
cellSize = cc.size(10, 10);
@property({tooltip: "1 for horizontal, 2 for vertical"})
direction = 1;
private _comps:cc.Sprite[];
private _compPool:cc.Sprite[];
private _compsCnt:number;
private _selectedIndex:number = -1;
constructor()
{
super();
}
onDestroy()
{
this._comps = null;
this._compPool = null;
}
setPageCnt(value:number)
{
if(value == this._compsCnt)
{
return;
}
if(!this._comps)
{
this._comps = [];
this._compPool = [];
this._compsCnt = 0;
}
for(let i = this._compsCnt; i < value; i++)
{
let comp = this._compPool.pop();
if(!comp)
{
const node = new cc.Node();
node.width = this.cellSize.width;
node.height = this.cellSize.height;
comp = node.addComponent(cc.Sprite);
comp.spriteFrame = this.unSelectedSpriteFrame;
}
comp.node.parent = this.node;
this._comps[i] = comp;
}
for(let i = this._compsCnt - 1; i >= value; i--)
{
const comp = this._comps.pop();
comp.node.removeFromParent();
this._compPool.push(comp);
}
this._compsCnt = value;
this.updateLayout();
}
private updateLayout()
{
//以锚点(0.5, 0.5)计算父容器和子节点位置
if(this.direction == 1)
{
const deltaX = this.cellSize.width + this.spacing;
this.node.width = (this._compsCnt - 1) * deltaX;
this.node.height = this.cellSize.height;
const halfWidth = this.node.width / 2;
this._comps.forEach((comp, idx) => {
comp.node.x = idx * deltaX - halfWidth;
comp.node.y = 0;
});
}
else
{
const deltaY = this.cellSize.height + this.spacing;
this.node.width = this.cellSize.width;
this.node.height = (this._compsCnt - 1) * deltaY;
const halfHeight = this.node.height / 2;
this._comps.forEach((comp, idx) => {
comp.node.x = 0;
comp.node.y = idx * deltaY - halfHeight;
});
}
}
turningTo(pageIdx:number)
{
if(pageIdx == this._selectedIndex)
{
return;
}
if(this._selectedIndex != -1 && this._selectedIndex < this._compsCnt)
{
this._comps[this._selectedIndex].spriteFrame = this.unSelectedSpriteFrame;
}
this._selectedIndex = pageIdx;
this._comps[pageIdx].spriteFrame = this.selectedSpriteFrame;
}
}

213
components/scrollpage.ts Normal file
View File

@@ -0,0 +1,213 @@
import { ScrollView, ScrollViewParams, ScrollDirection } from "./scrollview";
import { Action1 } from "../../const";
import { TimerMgr } from "../timer/timer_mgr";
import { gen_handler } from "../util";
//参考自engine/CCPageView
export class ScrollPage extends ScrollView
{
private _curPageIdx:number;
private _lastPageIdx:number;
private _pageTurningSpeed:number;
private _scrollThreshold:number;
private _autoPageTurningThreshold:number;
private _scrollCenterOffsetX:number[]; // 每一个页面居中时需要的偏移量X
private _scrollCenterOffsetY:number[]; // 每一个页面居中时需要的偏移量Y
private _onTurning:(pageIndex:number) => void;
private _onScrolling:Action1;
private _timer:number;
constructor(params:ScrollPageParams)
{
super(params);
this._onTurning = params.on_turning;
this._onScrolling = params.on_scrolling;
this._curPageIdx = 0;
this._lastPageIdx = -1;
this._pageTurningSpeed = 0.3;
this._scrollThreshold = 0.5;
this._autoPageTurningThreshold = 100;
this._scrollCenterOffsetX = [];
this._scrollCenterOffsetY = [];
}
scroll_to_page(idx:number, time = 0, delayTurning = false)
{
if (idx < 0 || idx >= this.packItems.length)
{
return;
}
this._curPageIdx = idx;
this.scrollview.scrollToOffset(this._moveOffsetValue(idx), time, true);
this.render();
//用以在onTurning事件回调中获取scrollViewItem
if(delayTurning) {
this._timer = TimerMgr.getInst().once(0, gen_handler(this._dispatchPageTurningEvent, this), "ScrollPage, scroll_to_page timer", this.scrollview.node);
}
else {
this._dispatchPageTurningEvent();
}
}
get page_index()
{
return this._curPageIdx;
}
protected on_scrolling()
{
super.on_scrolling();
if(this._onScrolling) {
this._onScrolling.call(this.cb_host);
}
}
protected handle_release_logic()
{
super.handle_release_logic();
let bounceBackStarted = this.scrollview._startBounceBackIfNeeded();
let moveOffset = this._touchBeganPosition.sub(this._touchEndPosition);
let dragDirection = this.get_drag_direction(moveOffset);
//点击无滑动
if(dragDirection === 0) {
return;
}
if(!this.packItems || !this.packItems.length)
{
return;
}
if (bounceBackStarted) {
if (dragDirection === 0) {
return;
}
if (dragDirection > 0) {
this._curPageIdx = this.packItems.length - 1;
}
else {
this._curPageIdx = 0;
}
}
else {
let index = this._curPageIdx, nextIndex = index + dragDirection;
let timeInSecond = this._pageTurningSpeed * Math.abs(index - nextIndex);
if (nextIndex >= 0 && nextIndex < this.packItems.length) {
if (this._isScrollable(moveOffset, index, nextIndex)) {
this.scroll_to_page(nextIndex, timeInSecond);
return;
}
else {
let touchMoveVelocity = this.scrollview._calculateTouchMoveVelocity();
if (this._isQuicklyScrollable(touchMoveVelocity)) {
this.scroll_to_page(nextIndex, timeInSecond);
return;
}
}
}
this.scroll_to_page(index, timeInSecond);
}
}
private _dispatchPageTurningEvent() {
if (this._lastPageIdx === this._curPageIdx) return;
this._lastPageIdx = this._curPageIdx;
if(this._onTurning)
{
this._onTurning.call(this.cb_host, this._curPageIdx);
}
}
// 是否超过自动滚动临界值
private _isScrollable(offset, index, nextIndex) {
let curPageCenter, nextPageCenter;
if (this.dir === ScrollDirection.Horizontal) {
curPageCenter = this._scrollCenterOffsetX[index];
nextPageCenter = this._scrollCenterOffsetX[nextIndex];
return Math.abs(offset.x) >= Math.abs(curPageCenter - nextPageCenter) * this._scrollThreshold;
}
else if (this.dir === ScrollDirection.Vertical) {
curPageCenter = this._scrollCenterOffsetY[index];
nextPageCenter = this._scrollCenterOffsetY[nextIndex];
return Math.abs(offset.y) >= Math.abs(curPageCenter - nextPageCenter) * this._scrollThreshold;
}
}
// 快速滑动
private _isQuicklyScrollable(touchMoveVelocity) {
if (this.dir === ScrollDirection.Horizontal) {
if (Math.abs(touchMoveVelocity.x) > this._autoPageTurningThreshold) {
return true;
}
}
else if (this.dir === ScrollDirection.Vertical) {
if (Math.abs(touchMoveVelocity.y) > this._autoPageTurningThreshold) {
return true;
}
}
return false;
}
// 通过 idx 获取偏移值数值
private _moveOffsetValue(idx) {
let offset = cc.v2(0, 0);
if (this.dir === ScrollDirection.Horizontal) {
offset.x = this._scrollCenterOffsetX[idx];
}
else if (this.dir === ScrollDirection.Vertical) {
offset.y = this._scrollCenterOffsetY[idx];
}
return offset;
}
protected layout_items(start:number)
{
super.layout_items(start);
const scrollWidth = this.width;
const scrollHeight = this.height;
for (let i = 0, len = this.packItems.length; i < len; ++i) {
//居中显示每个page
if (this.dir === ScrollDirection.Horizontal) {
this._scrollCenterOffsetX[i] = Math.abs(this.packItems[i].x) - (Math.abs(scrollWidth - this.packItems[i].width) >> 1);
}
else {
this._scrollCenterOffsetY[i] = Math.abs(this.packItems[i].y) - (Math.abs(scrollHeight - this.packItems[i].height) >> 1);
}
}
}
remove_data(index:number, count:number = 1)
{
super.remove_data(index, count);
//选中项被删除
const maxPageIdx = this.packItems.length - 1;
if(this._curPageIdx > maxPageIdx)
{
this.scroll_to_page(maxPageIdx, 0);
}
}
destroy()
{
this._onTurning = null;
this._onScrolling = null;
this._scrollCenterOffsetX = null;
this._scrollCenterOffsetY = null;
if(this._timer) {
TimerMgr.getInst().remove(this._timer);
this._timer = null;
}
super.destroy()
}
}
export interface ScrollPageParams extends ScrollViewParams
{
on_turning?:(pageIndex:number) => void;
on_scrolling?:Action1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
const {ccclass, property} = cc._decorator;
@ccclass
export class ScrollViewItem extends cc.Component
{
// constructor()
// {
// super();
// }
onInit(key:string)
{
// cc.log("item初如化");
}
onUnInit(key:string)
{
// cc.log("item析构");
}
onSetData(key:string, data:any, index:number, is_measure:boolean):[number, number]
{
// cc.log("item设置数据");
return [0, 0];
}
onSetSelect(key:string, is_select:boolean, index:number)
{
// cc.log("item选中状态改变");
}
onRecycle(key:string, data:any, is_measure:boolean)
{
// cc.log("item被回收");
}
onSelected(key:string, data:any, index:number)
{
// cc.log("item被选中");
}
onTouchEnd(key:string, data:any, touchPos:cc.Vec2, index:number)
{
// cc.log("item非滑动状态下被点击");
}
onBecameInvisible()
{
// cc.log("item从父节点移除或不可见");
}
// get top()
// {
// return this.node.y + (1 - this.node.anchorY) * this.node.height;
// }
// get bottom()
// {
// return this.node.y - this.node.anchorY * this.node.height;
// }
// get left()
// {
// return this.node.x - this.node.anchorX * this.node.width;
// }
// get right()
// {
// return this.node.x + (1 - this.node.anchorX) * this.node.width;
// }
// set top(value:number)
// {
// this.node.y = value - (1 - this.node.anchorY) * this.node.height;
// }
// set bottom(value:number)
// {
// this.node.y = value + this.node.anchorY * this.node.height;
// }
// set left(value:number)
// {
// this.node.x = value + this.node.anchorX * this.node.width;
// }
// set right(value:number)
// {
// this.node.x = value - (1 - this.node.anchorX) * this.node.width;
// }
setLeftTop(left:number, top:number)
{
this.node.x = left + this.node.anchorX * this.node.width;
this.node.y = top - (1 - this.node.anchorY) * this.node.height;
}
}

164
components/star_rank.ts Normal file
View File

@@ -0,0 +1,164 @@
type StarOnTouchEnd = (index:number) => void;
const {ccclass, property} = cc._decorator;
@ccclass
export class StarRank extends cc.Component {
@property({type: cc.SpriteFrame})
lightStarSpriteFrame: cc.SpriteFrame = null;
@property({type: cc.SpriteFrame})
darkStarSpriteFrame: cc.SpriteFrame = null;
@property
spacing = 0;
@property
cellSize = cc.size(10, 10);
@property({tooltip: "1 for horizontal, 2 for vertical"})
direction = 1;
private _comps:cc.Sprite[];
private _compPool:cc.Sprite[];
private _totalCnt:number;
private _lightCnt:number;
private _touchCb:StarOnTouchEnd;
constructor()
{
super();
}
// onLoad()
// {
// this.setTotalCnt(5);
// this.setLightCnt(1);
// cc.log(this.node.x, this.node.y)
// }
onDestroy()
{
this._comps = null;
this._compPool = null;
this._touchCb = null;
}
setSpacing(value:number)
{
this.spacing = value;
}
setDirection(value:number)
{
this.direction = value;
}
setTotalCnt(value:number)
{
if(value < 0)
{
cc.log(`invalid totalStar value`);
return;
}
if(value == this._totalCnt)
{
return;
}
if(!this._comps)
{
this._comps = [];
this._compPool = [];
this._totalCnt = 0;
}
for(let i = this._totalCnt; i < value; i++)
{
let comp = this._compPool.pop();
if(!comp)
{
const node = new cc.Node();
node.width = this.cellSize.width;
node.height = this.cellSize.height;
comp = node.addComponent(cc.Sprite);
}
comp.node.parent = this.node;
this._comps[i] = comp;
comp.node.on(cc.Node.EventType.TOUCH_END, event => {
if(this._touchCb) {
this._touchCb(i);
}
}, this);
}
for(let i = this._totalCnt - 1; i >= value; i--)
{
const comp = this._comps.pop();
comp.node.targetOff(this);
comp.node.removeFromParent();
this._compPool.push(comp);
}
this._totalCnt = value;
this.updateLayout();
}
private updateLayout()
{
//以锚点(0.5, 0.5)计算父容器和子节点位置
if(this.direction == 1)
{
const deltaX = this.cellSize.width + this.spacing;
this.node.width = this._totalCnt * deltaX;
this.node.height = this.cellSize.height;
this._comps.forEach((comp, idx) => {
comp.node.x = idx * deltaX - 0.5 * (this._totalCnt - 1) * deltaX;
comp.node.y = 0;
});
}
else
{
const deltaY = this.cellSize.height + this.spacing;
this.node.width = this.cellSize.width;
this.node.height = this._totalCnt * deltaY;
this._comps.forEach((comp, idx) => {
comp.node.x = 0;
comp.node.y = idx * deltaY - 0.5 * (this._totalCnt - 1) * deltaY;
});
}
}
setLightCnt(value:number)
{
if(value < 0 || value > this._totalCnt)
{
cc.log(`invalid lightStar value`);
return;
}
if(value == this._lightCnt)
{
return;
}
this._lightCnt = value;
for(let i = 0; i < this._totalCnt; i++)
{
this._comps[i].spriteFrame = i < value ? this.lightStarSpriteFrame : this.darkStarSpriteFrame;
}
}
getLightCnt()
{
return this._lightCnt || 0;
}
setTouchCb(cb:StarOnTouchEnd)
{
this._touchCb = cb;
}
getComp(index:number)
{
return this._comps[index];
}
}

75
components/tabpage.ts Normal file
View File

@@ -0,0 +1,75 @@
import { EventTool } from "../event/event_tool";
export class TabPage extends EventTool
{
protected _node:cc.Node;
protected _parent:cc.Node;
protected _isSelected:boolean;
constructor(node:cc.Node)
{
super();
this._node = node;
this.onInit();
}
onInit()
{
}
setParent(parent:cc.Node)
{
this._parent = parent;
}
alignToParent()
{
this._node.setPosition(0, 0);
}
setSelected(value:boolean)
{
if(this._isSelected == value) {
return;
}
this._isSelected = value;
if(value && !this._node.parent) {
this._node.parent = this._parent;
this.onBecameVisible();
}
else if(!value && this._node.parent) {
this._node.removeFromParent(false);
this.onBecameInvisible();
}
}
protected onBecameVisible()
{
}
protected onBecameInvisible()
{
}
get selected()
{
return this._isSelected;
}
destroy()
{
this.onDestroy();
this._parent = null;
if(cc.isValid(this._node)) {
this._node.destroy();
this._node = null;
}
}
onDestroy()
{
}
}

202
components/tabview.ts Normal file
View File

@@ -0,0 +1,202 @@
import { loader_mgr } from "../loader/loader_mgr";
import { TabPage } from "./tabpage";
import { gen_handler } from "../util";
export class TabView
{
private _tabContainer:cc.Node;
private _pageContainer:cc.Node;
private _tabBg:cc.Node;
private _pageParamMap:Map<string, PageParams>;
private _btnMap:Map<string, cc.Node>;
private _pageMap:Map<string, TabPage>;
private _loadingMap:Map<string, boolean>;
private _selectedTab:string;
private _onTabSelected:TabSelectedCb;
constructor(params:TabParams)
{
this._onTabSelected = params.onTabSelected;
this._pageParamMap = new Map();
this._btnMap = new Map();
this._pageMap = new Map();
this._loadingMap = new Map();
this._tabContainer = params.tabContainer;
this._pageContainer = params.pageContainer;
this._tabBg = params.tabBg;
params.pages.forEach(page => {
const btn = this._tabContainer.getChildByName(page.btnPath);
const normal = btn.getChildByName("normal");
const select = btn.getChildByName("select");
normal.active = true;
select.active = false;
this._btnMap.set(page.key, btn);
this._pageParamMap.set(page.key, page);
normal.on(cc.Node.EventType.TOUCH_END, this.select.bind(this, page.key), this);
if(page.btnTxt) {
normal.getChildByName("txt").getComponent(cc.Label).string = page.btnTxt;
select.getChildByName("txt").getComponent(cc.Label).string = page.btnTxt;
}
});
if(params.initTab) {
this.select(params.initTab);
}
}
select(key:string)
{
if(key == this._selectedTab) {
return;
}
const param = this._pageParamMap.get(key);
if(!param) {
cc.log(`TabView, select, invalid tabKey=${key}`);
return;
}
if(this._selectedTab) {
this.setBtnSelected(this._selectedTab, false);
this.setPageSelected(this._selectedTab, false);
}
this.setBtnSelected(key, true);
this._selectedTab = key;
if(this._onTabSelected) {
this._onTabSelected(key);
}
//adjust tab btns z-index
if(this._tabBg) {
this.adjustOrder();
}
//load tabpage
const page = this._pageMap.get(key);
if(page) {
page.setSelected(true);
}
else {
const isLoading = this._loadingMap.get(key);
if(isLoading) {
return;
}
this._loadingMap.set(key, true);
loader_mgr.get_inst().loadRes(param.resPath, gen_handler((pKey:string, res:cc.Prefab) => {
//已经destroy了
if(!this._loadingMap) {
return;
}
this._loadingMap.delete(pKey);
if(this._selectedTab != pKey) {
cc.log(`TabView, loadPage done, selectedTab=${this._selectedTab}, argTab=${pKey}`);
return;
}
const node = cc.instantiate(res);
const pageInst = new param.clazz(node);
pageInst.setParent(this._pageContainer);
pageInst.alignToParent();
pageInst.setSelected(true);
this._pageMap.set(pKey, pageInst);
}, this, key));
}
}
getPage(key:string)
{
return this._pageMap.get(key);
}
getSelectedPage()
{
if(this._selectedTab) {
return this._pageMap.get(this._selectedTab);
}
return null;
}
private setBtnSelected(key:string, selected:boolean)
{
const btn = this._btnMap.get(key);
const normal = btn.getChildByName("normal");
const select = btn.getChildByName("select");
normal.active = !selected;
select.active = selected;
}
private setPageSelected(key:string, selected:boolean)
{
const page = this._pageMap.get(key);
if(page) {
page.setSelected(selected);
}
}
private adjustOrder()
{
const selectedBtn = this._btnMap.get(this._selectedTab);
const childCnt = this._tabContainer.childrenCount;
selectedBtn.setSiblingIndex(childCnt - 1);
this._tabBg.setSiblingIndex(childCnt - 2);
}
updateBtnTxts(values:{key:string, txt:string}[])
{
values.forEach(v => {
const btn = this._btnMap.get(v.key);
if(btn) {
btn.getChildByName("normal").getChildByName("txt").getComponent(cc.Label).string = v.txt;
btn.getChildByName("select").getChildByName("txt").getComponent(cc.Label).string = v.txt;
}
});
}
destroy()
{
this._pageParamMap.clear();
this._pageParamMap = null;
this._btnMap.forEach(btn => {
const normal = btn.getChildByName("normal");
normal.off(cc.Node.EventType.TOUCH_END);
});
this._btnMap.clear();
this._btnMap = null;
this._pageMap.forEach(page => {
page.destroy();
});
this._pageMap.clear();
this._pageMap = null;
this._loadingMap.clear();
this._loadingMap = null;
this._tabContainer = null;
this._pageContainer = null;
this._tabBg = null;
this._selectedTab = null;
this._onTabSelected = null;
}
}
export type TabSelectedCb = (key:string) => void;
type TabParams = {
tabContainer:cc.Node;
pageContainer:cc.Node;
tabBg?:cc.Node;
pages:PageParams[];
initTab?:string;
onTabSelected?:TabSelectedCb;
}
type PageParams = {
key:string;
btnPath:string;
btnTxt?:string;
resPath:string;
clazz:new(node:cc.Node) => TabPage;
}

View File

@@ -0,0 +1,361 @@
import { loader_mgr } from "../loader/loader_mgr";
import { gen_handler } from "../util";
type DB_EVENT_HANDLER = (event:dragonBones.EventObject, state:dragonBones.AnimationState) => void;
const EVENT_DB_ASSET_LOADED = "EVENT_DB_ASSET_LOADED";
const BasePathForSke = (path:string) => {
return `dragonbones/${path}_ske`;
};
const BasePathForTex = (path:string) => {
return `dragonbones/${path}_tex`;
};
export class DragonBoneFactory
{
private static _inst:DragonBoneFactory;
private _loadingMap:Map<string, dragonBones.ArmatureDisplay[]>
private _eventMap:Map<string, cc.EventTarget>;
private _stateMap:Map<string, dragonBones.AnimationState>;
private _compPool:dragonBones.ArmatureDisplay[];
private _refMap:Map<string, number>;
private _resMap:Map<string, string>;
private constructor()
{
this._compPool = [];
this._loadingMap = new Map();
this._eventMap = new Map();
this._stateMap = new Map();
this._resMap = new Map();
this._refMap = new Map();
}
static getInst()
{
if(!this._inst)
{
this._inst = new DragonBoneFactory();
}
return this._inst;
}
buildDB(params:ArmatureDisplayParams)
{
let comp = this._compPool.pop();
let node:cc.Node;
if(comp)
{
node = comp.node;
}
else
{
node = new cc.Node();
comp = node.addComponent(dragonBones.ArmatureDisplay);
//save reference count for auto release
const path = params.path;
this._resMap.set(comp.uuid, path);
const refCnt = (this._refMap.get(path) || 0) + 1;
this._refMap.set(path, refCnt);
cc.log(`DragonBoneFactory, increaseRef, path=${path}, ref=${refCnt}`);
}
if(params.parent)
{
node.parent = params.parent;
node.x = params.x || 0;
node.y = params.y || 0;
}
node.active = params.active != null ? params.active : true;
// cc.log("buildArmatureDisplay", node.getContentSize(), node.getPosition(), node.getAnchorPoint());
const path = params.path;
const skePath = BasePathForSke(path);
const texPath = BasePathForTex(path);
const dragonBonesAsset = cc.loader.getRes(skePath, dragonBones.DragonBonesAsset);
const dragonBonesAtlasAsset = cc.loader.getRes(texPath, dragonBones.DragonBonesAtlasAsset);
const texture2D = cc.loader.getRes(texPath, cc.Texture2D);
if(dragonBonesAsset && dragonBonesAtlasAsset && texture2D)
{
this.attachAsset(comp, params, dragonBonesAsset, dragonBonesAtlasAsset, texture2D);
}
else
{
const assets:DBAsset[] = [
{path:skePath, alias:"dragonBonesAsset", type:dragonBones.DragonBonesAsset},
{path:texPath, alias:"dragonBonesAtlasAsset", type:dragonBones.DragonBonesAtlasAsset},
{path:texPath, alias:"texture2D", type:cc.Texture2D},
];
this.loadAsset(comp, params, assets);
}
return comp;
}
private loadAsset(comp:dragonBones.ArmatureDisplay, params:ArmatureDisplayParams, assets:DBAsset[])
{
const path = params.path;
//同一路径龙骨资源只加载一次
let loadings = this._loadingMap.get(path);
if(!loadings)
{
loadings = [];
this._loadingMap.set(path, loadings);
}
loadings.push(comp)
if(loadings.length > 1)
{
return;
}
// cc.log(`DragonBoneFactory, loadAsset, path=${path}`);
loader_mgr.get_inst().loadResArray(assets.map(a => a.path), gen_handler(res => {
const dragonBonesAsset = res[assets[0].alias];
const dragonBonesAtlasAsset = res[assets[1].alias];
const texture2D = res[assets[2].alias];
const lds = this._loadingMap.get(path);
if(lds) {
lds.forEach(p => {
if(!p || !cc.isValid(p.node)) {
return;
}
if(dragonBonesAsset && dragonBonesAtlasAsset && texture2D) {
this.attachAsset(p, params, dragonBonesAsset, dragonBonesAtlasAsset, texture2D);
p.node.emit(EVENT_DB_ASSET_LOADED);
}
});
lds.length = 0;
this._loadingMap.delete(path);
}
}), assets.map(a => a.type), assets.map(a => a.alias));
}
private attachAsset(comp:dragonBones.ArmatureDisplay, params:ArmatureDisplayParams, dragonBonesAsset:dragonBones.DragonBonesAsset, dragonBonesAtlasAsset:dragonBones.DragonBonesAtlasAsset, texture2D:cc.Texture2D)
{
comp.dragonAsset = dragonBonesAsset;
dragonBonesAtlasAsset.texture = texture2D;
comp.dragonAtlasAsset = dragonBonesAtlasAsset;
comp.armatureName = params.armatureName;
comp.addEventListener(dragonBones.EventObject.COMPLETE, event => {
// cc.log("dragonbone complete event", event, comp.uuid);
const state = this._stateMap.get(comp.uuid);
if(params.onComplete) {
params.onComplete(event, state);
}
else {
this.invokeDBEvent(comp, dragonBones.EventObject.COMPLETE, event, state);
}
});
comp.addEventListener(dragonBones.EventObject.START, event => {
// cc.log("dragonbone start event", event, comp.uuid);
const state = this._stateMap.get(comp.uuid);
if(params.onStart) {
params.onStart(event, state);
}
else {
this.invokeDBEvent(comp, dragonBones.EventObject.START, event, state);
}
});
}
private decreaseRef(comp:dragonBones.ArmatureDisplay)
{
const path = this._resMap.get(comp.uuid);
if(path)
{
this._resMap.delete(comp.uuid);
const refCnt = (this._refMap.get(path) || 0) - 1;
this._refMap.set(path, refCnt);
cc.log(`DragonBoneFactory, decreaseRef, path=${path}, ref=${refCnt}`);
if(refCnt == 0)
{
this._refMap.delete(path);
this.releaseAsset(path);
}
}
}
releaseDB(comp:dragonBones.ArmatureDisplay, recycle = false)
{
const uuid = comp.uuid;
this._stateMap.delete(uuid);
comp.node.off(EVENT_DB_ASSET_LOADED);
comp.removeEventListener(dragonBones.EventObject.COMPLETE);
this.removeDBEvent(comp, dragonBones.EventObject.COMPLETE);
comp.removeEventListener(dragonBones.EventObject.START);
this.removeDBEvent(comp, dragonBones.EventObject.START);
comp.stopAnimation("");
comp.dragonAtlasAsset = null;
comp.dragonAsset = null;
comp.armatureName = null;
comp.animationName = null;
const node = comp.node;
if(recycle)
{
node.removeFromParent();
this._compPool.push(comp);
}
else
{
this.decreaseRef(comp);
node.destroy();
}
}
playDB(comp:dragonBones.ArmatureDisplay, animationName:string, playTimes:number = 1, armatureName?:string)
{
const node = comp.node;
if(!node)
{
cc.warn(`db has no associated node, animationName=${animationName}`);
return;
}
if(!node.activeInHierarchy)
{
cc.warn(`db associated node is inactive, animationName=${animationName}`);
return;
}
if(!comp.dragonAsset)
{
// cc.log(`DragonBoneFactory, playDB async, animationName=${animationName}`);
//ArmatureDisplay资源加载完成后才能监听事件所以这里必须要用node监听
comp.node.on(EVENT_DB_ASSET_LOADED, () => {
if(armatureName)
{
comp.armatureName = armatureName;
}
const state = comp.playAnimation(animationName, playTimes);
this._stateMap.set(comp.uuid, state);
// cc.log(`DragonBoneFactory, onAssetLoaded, playDB, animationName=${animationName}`);
});
// cc.log("dragonbone play before loaded");
return;
}
if(armatureName)
{
comp.armatureName = armatureName;
}
const state = comp.playAnimation(animationName, playTimes);
this._stateMap.set(comp.uuid, state);
// cc.log(`DragonBoneFactory, playDB, animationName=${animationName}`);
}
addStartEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
{
this.addDBEvent(comp, dragonBones.EventObject.START, handler, target);
}
onceStartEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
{
this.onceDBEvent(comp, dragonBones.EventObject.START, handler, target);
}
removeStartEvent(comp:dragonBones.ArmatureDisplay, handler?:DB_EVENT_HANDLER, target?:any)
{
this.removeDBEvent(comp, dragonBones.EventObject.START, handler, target);
}
addCompleteEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
{
this.addDBEvent(comp, dragonBones.EventObject.COMPLETE, handler, target);
}
onceCompleteEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
{
this.onceDBEvent(comp, dragonBones.EventObject.COMPLETE, handler, target);
}
removeCompleteEvent(comp:dragonBones.ArmatureDisplay, handler?:DB_EVENT_HANDLER, target?:any)
{
this.removeDBEvent(comp, dragonBones.EventObject.COMPLETE, handler, target);
}
//ArmatureDisplay资源加载完成前无法监听事件用此方法可代替
addDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, handler:DB_EVENT_HANDLER, target?:any)
{
const key = comp.uuid + "_" + eventType;
let eventTarget = this._eventMap.get(key);
if(!eventTarget)
{
eventTarget = new cc.EventTarget();
this._eventMap.set(key, eventTarget);
}
return eventTarget.on(eventType, handler, target);
}
onceDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, handler:DB_EVENT_HANDLER, target?:any)
{
const key = comp.uuid + "_" + eventType;
let eventTarget = this._eventMap.get(key);
if(!eventTarget)
{
eventTarget = new cc.EventTarget();
this._eventMap.set(key, eventTarget);
}
return eventTarget.once(eventType, handler, target);
}
removeDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, handler?:DB_EVENT_HANDLER, target?:any)
{
const key = comp.uuid + "_" + eventType;
const eventTarget = this._eventMap.get(key);
if(!eventTarget)
{
return;
}
this._eventMap.delete(key);
eventTarget.off(eventType, handler, target);
}
private invokeDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, ...params)
{
const key = comp.uuid + "_" + eventType;
const eventTarget = this._eventMap.get(key);
if(!eventTarget)
{
return;
}
eventTarget.emit(eventType, ...params);
}
releaseAsset(path:string)
{
const skePath = BasePathForSke(path);
const texPath = BasePathForTex(path);
cc.loader.releaseRes(skePath, dragonBones.DragonBonesAsset);
cc.loader.releaseRes(texPath, dragonBones.DragonBonesAtlasAsset);
cc.loader.releaseRes(texPath, cc.Texture2D);
cc.log(`DragonBoneFactory, releaseAsset, path=${skePath}`);
}
releaseAll()
{
this._loadingMap.clear();
this._eventMap.clear();
this._stateMap.clear();
this._compPool.forEach(comp => {
this.decreaseRef(comp);
comp.destroy();
});
this._compPool.length = 0;
}
}
interface ArmatureDisplayParams
{
path:string; //龙骨导出文件路径
armatureName:string; //骨架名称
onComplete?:DB_EVENT_HANDLER;
onStart?:DB_EVENT_HANDLER;
parent?:cc.Node;
x?:number;
y?:number;
active?:boolean;
}
interface DBAsset
{
path:string;
alias:string;
type:typeof cc.Asset;
}

195
effect/particle_factory.ts Normal file
View File

@@ -0,0 +1,195 @@
import { loader_mgr } from "../loader/loader_mgr";
import { gen_handler } from "../util";
const EVENT_PARTICLE_ASSET_LOADED = "EVENT_PARTICLE_ASSET_LOADED";
const BasePathForParticle = (path:string) => {
return `particles/${path}`;
};
export class ParticleFactory {
private static _inst:ParticleFactory;
private _compPool:cc.ParticleSystem[];
private _loadingMap:Map<string, cc.ParticleSystem[]>;
private _refMap:Map<string, number>;
private _resMap:Map<string, string>;
private constructor() {
this._compPool = [];
this._loadingMap = new Map();
this._resMap = new Map();
this._refMap = new Map();
}
static getInst() {
if(!this._inst) {
this._inst = new ParticleFactory();
}
return this._inst;
}
buildParticle(params:ParticleParams) {
let comp = this._compPool.pop();
let node:cc.Node;
if(comp) {
node = comp.node;
}
else {
node = new cc.Node();
comp = node.addComponent(cc.ParticleSystem);
//save reference count for auto release
const path = params.path;
this._resMap.set(comp.uuid, path);
const refCnt = (this._refMap.get(path) || 0) + 1;
this._refMap.set(path, refCnt);
cc.log(`ParticleFactory, increaseRef, path=${path}, ref=${refCnt}`);
}
comp.playOnLoad = false;
comp.autoRemoveOnFinish = false;
if(params.parent) {
node.parent = params.parent;
node.x = params.x || 0;
node.y = params.y || 0;
}
const path = params.path;
const basePath = BasePathForParticle(path);
const particleAsset:cc.ParticleAsset = cc.loader.getRes(basePath, cc.ParticleAsset);
const spriteFrame:cc.SpriteFrame = cc.loader.getRes(basePath, cc.SpriteFrame);
if(particleAsset && spriteFrame) {
this.attachAsset(comp, particleAsset, spriteFrame);
}
else {
const assets:ParticleAsset[] = [
{path:basePath, alias:"particleAsset", type:cc.ParticleAsset},
{path:basePath, alias:"spriteFrame", type:cc.SpriteFrame},
];
this.loadAsset(comp, path, assets);
}
return comp;
}
private loadAsset(comp:cc.ParticleSystem, path:string, assets:ParticleAsset[]) {
//同一路径资源只加载一次
let loadings = this._loadingMap.get(path);
if(!loadings) {
loadings = [];
this._loadingMap.set(path, loadings);
}
loadings.push(comp);
if(loadings.length > 1) {
return;
}
cc.log(`ParticleFactory, loadAsset, path=${assets[0].path}`);
loader_mgr.get_inst().loadResArray(assets.map(a => a.path), gen_handler(res => {
const particleAsset:cc.ParticleAsset = res[assets[0].alias];
const spriteFrame:cc.SpriteFrame = res[assets[1].alias];
const lds = this._loadingMap.get(path);
if(lds) {
lds.forEach(p => {
if(!p || !cc.isValid(p.node)) {
return;
}
if(particleAsset && spriteFrame) {
this.attachAsset(p, particleAsset, spriteFrame);
p.node.emit(EVENT_PARTICLE_ASSET_LOADED);
}
});
lds.length = 0;
this._loadingMap.delete(path);
}
}), assets.map(a => a.type), assets.map(a => a.alias));
}
private attachAsset(comp:cc.ParticleSystem, particleAsset:cc.ParticleAsset, spriteFrame:cc.SpriteFrame) {
comp.file = particleAsset;
comp.spriteFrame = spriteFrame;
}
play(comp:cc.ParticleSystem) {
const node = comp.node;
if(!node) {
cc.warn(`cc.ParticleSystem has no associated node`);
return;
}
if(!node.activeInHierarchy) {
cc.warn(`cc.ParticleSystem associated node is inactive`);
return;
}
if(!comp.file) {
cc.log(`ParticleSystem, play async`);
comp.node.on(EVENT_PARTICLE_ASSET_LOADED, () => {
comp.resetSystem();
cc.log(`ParticleSystem, onAssetLoaded play`);
});
return;
}
comp.resetSystem();
cc.log(`ParticleSystem, play`);
}
private decreaseRef(comp:cc.ParticleSystem)
{
const path = this._resMap.get(comp.uuid);
if(path)
{
this._resMap.delete(comp.uuid);
const refCnt = (this._refMap.get(path) || 0) - 1;
this._refMap.set(path, refCnt);
cc.log(`ParticleFactory, decreaseRef, path=${path}, ref=${refCnt}`);
if(refCnt == 0)
{
this._refMap.delete(path);
this.releaseAsset(path);
}
}
}
releaseParticle(comp:cc.ParticleSystem, recycle = false) {
comp.node.off(EVENT_PARTICLE_ASSET_LOADED);
comp.stopSystem();
comp.file = null;
comp.spriteFrame = null;
const node = comp.node;
if(recycle) {
node.removeFromParent();
this._compPool.push(comp);
}
else {
this.decreaseRef(comp);
node.destroy();
}
}
releaseAsset(path:string) {
const basePath = BasePathForParticle(path);
cc.loader.releaseRes(basePath, cc.ParticleAsset);
cc.loader.releaseRes(basePath, cc.SpriteFrame);
cc.log(`ParticleFactory, releaseAsset, path=${basePath}`);
}
releaseAll() {
this._loadingMap.clear();
this._compPool.forEach(comp => {
this.decreaseRef(comp);
comp.destroy();
});
this._compPool.length = 0;
}
}
interface ParticleParams {
path:string; //plist文件路径
parent?:cc.Node;
x?:number;
y?:number;
}
interface ParticleAsset {
path:string;
alias:string;
type:typeof cc.Asset;
}

View File

@@ -1,84 +1,319 @@
export class event_mgr
{
private static inst:event_mgr;
private listeners:any; //Event_Name => cb[]
private constructor()
{
this.listeners = {};
}
static get_inst():event_mgr
{
if(!this.inst)
{
this.inst = new event_mgr();
}
return this.inst;
}
fire(event:Event_Name, ...params:any[]):void
{
let cbs:any[] = this.listeners[event];
if(!cbs)
{
return;
}
for(let i:number = 0, len:number = cbs.length; i < len; i += 2)
{
let cb:any = cbs[i];
let host:any = cbs[i+1];
cb.call(host, ...params);
}
}
add(event:Event_Name, cb:Function, host:any = null):void
{
let cbs:any[] = this.listeners[event];
if(!cbs)
{
this.listeners[event] = cbs = [];
}
cbs.push(cb, host);
}
remove(event:Event_Name, cb:Function)
{
let cbs:any[] = this.listeners[event];
if(!cbs)
{
return;
}
let index:number = cbs.indexOf(cb);
if(index < 0)
{
cc.warn("event_mgr remove", event, ", but cb not exists!");
return;
}
cbs.splice(index, 2);
}
clear()
{
for(let key in this.listeners)
{
this.listeners[key].length = 0;
}
this.listeners = {};
}
}
/**事件名称定义*/
export enum Event_Name {
USER_INFO_CHANGED,
COIN_CHANGED,
PASS_CHANGED,
GRADE_CHANGED,
SHAREHINT_CHANGED,
GAME_LEVEL_CHANGED,
UI_SHOW,
UI_HIDE,
LEVEL_DATA_LOADED,
MUTE_MUSIC,
UNMUTE_MUSIC,
export type MyEvnetHandler = (...params) => void;
class MyEventListeners
{
public handlers:MyEvnetHandler[];
public targets:any[];
public isInvoking:boolean;
private containCanceled:boolean;
constructor()
{
this.handlers = [];
this.targets = [];
this.isInvoking = false;
this.containCanceled = false;
}
add(handler:MyEvnetHandler, target)
{
this.handlers.push(handler);
this.targets.push(target);
}
remove(index:number)
{
this.handlers.splice(index, 1);
this.targets.splice(index, 1);
}
removeByTarget(target)
{
const targets = this.targets;
const handlers = this.handlers;
for(let i = targets.length - 1; i >= 0; i--) {
if(targets[i] == target) {
targets.splice(i, 1);
handlers.splice(i, 1);
}
}
}
removeByHandler(handler:MyEvnetHandler)
{
const handlers = this.handlers;
const targets = this.targets;
for(let i = handlers.length - 1; i >= 0; i--) {
if(handlers[i] == handler) {
handlers.splice(i, 1);
targets.splice(i, 1);
}
}
}
removeByHandlerTarget(handler:MyEvnetHandler, target)
{
const handlers = this.handlers;
const targets = this.targets;
for(let i = handlers.length - 1; i >= 0; i--) {
if(handlers[i] == handler && targets[i] == target) {
handlers.splice(i, 1);
targets.splice(i, 1);
}
}
}
removeAll()
{
this.handlers.length = 0;
this.targets.length = 0;
}
cancel(index:number)
{
this.handlers[index] = null;
this.targets[index] = null;
this.containCanceled = true;
}
cancelByTarget(target)
{
const targets = this.targets;
const handlers = this.handlers;
for(let i = targets.length - 1; i >= 0; i--) {
if(targets[i] == target) {
targets[i] = null;
handlers[i] = null;
}
}
this.containCanceled = true;
}
cancelByHandler(handler:MyEvnetHandler)
{
const handlers = this.handlers;
const targets = this.targets;
for(let i = handlers.length - 1; i >= 0; i--) {
if(handlers[i] == handler) {
handlers[i] = null;
targets[i] = null;
}
}
this.containCanceled = true;
}
cancelByHandlerTarget(handler:MyEvnetHandler, target)
{
const handlers = this.handlers;
const targets = this.targets;
for(let i = handlers.length - 1; i >= 0; i--) {
if(handlers[i] == handler && targets[i] == target) {
handlers[i] = null;
targets[i] = null;
}
}
this.containCanceled = true;
}
cancelAll()
{
const handlers = this.handlers;
const targets = this.targets;
for(let i = handlers.length - 1; i >= 0; i--) {
handlers[i] = null;
targets[i] = null;
}
this.containCanceled = true;
}
has(handler:MyEvnetHandler, target)
{
const handlers = this.handlers;
const targets = this.targets;
for(let i = handlers.length - 1; i >= 0; i--) {
if(handlers[i] == handler && targets[i] == target) {
return true;
}
}
return false;
}
purgeCanceled()
{
if(this.containCanceled) {
this.removeByHandler(null);
this.containCanceled = false;
}
}
isEmpty()
{
return this.handlers.length == 0;
}
}
export class event_mgr
{
private static inst:event_mgr;
private eventMap:Map<Event_Name, MyEventListeners>;
private constructor()
{
this.eventMap = new Map();
}
static get_inst():event_mgr
{
if(!this.inst) {
this.inst = new event_mgr();
}
return this.inst;
}
fire(event:Event_Name, ...params)
{
// EventHelper.log(`EventMgr`, `fire event ${event}`);
const listeners = this.eventMap.get(event);
if(!listeners || listeners.isEmpty()) {
return;
}
//事件处理函数中可能会删除事件,导致循环出错
listeners.isInvoking = true;
const handlers = listeners.handlers;
const targets = listeners.targets;
for(let i = 0, len = handlers.length; i < len; i++) {
const handler = handlers[i];
const target = targets[i];
if(!handler) {
continue;
}
//如果target是cc.Component则在其节点有效时才调用事件函数
if(target && (<cc.Component>target).node) {
const node = (target as cc.Component).node;
if(cc.isValid(node)) {
handler.call(target, ...params);
}
else {
listeners.cancelByTarget(target);
}
}
else {
handler.call(target, ...params);
}
}
//循环结束后再删除
listeners.isInvoking = false;
listeners.purgeCanceled();
}
has(event:Event_Name, handler:MyEvnetHandler, target = null)
{
let listeners = this.eventMap.get(event);
if(!listeners) {
return false;
}
if(handler) {
return listeners.has(handler, target);
}
//检查event是否有监听者
if(listeners.isInvoking) {
const handlers = listeners.handlers;
for(let i = 0, len = handlers.length; i < len; i++) {
if(handlers[i]) {
return true;
}
}
return false;
}
else {
return !listeners.isEmpty();
}
}
add(event:Event_Name, handler:MyEvnetHandler, target = null)
{
// EventHelper.log(`EventMgr`, `add, event = ${event}`);
let listeners = this.eventMap.get(event);
if(!listeners) {
listeners = new MyEventListeners();
this.eventMap.set(event, listeners);
}
listeners.add(handler, target);
}
once(event:Event_Name, handler:MyEvnetHandler, target = null)
{
// EventHelper.log(`EventMgr`, `once, event = ${event}`);
let wrapperCb:MyEvnetHandler;
wrapperCb = (...params) => {
this.remove(event, wrapperCb, target);
handler.call(target, ...params);
};
this.add(event, wrapperCb, target);
}
remove(event:Event_Name, handler:MyEvnetHandler, target = null)
{
const listeners = this.eventMap.get(event);
if(!listeners || listeners.isEmpty()) {
return;
}
if(target) {
if(listeners.isInvoking) {
listeners.cancelByHandlerTarget(handler, target);
}
else {
listeners.removeByHandlerTarget(handler, target);
}
}
else {
if(listeners.isInvoking) {
listeners.cancelByHandler(handler);
}
else {
listeners.removeByHandler(handler);
}
}
}
removeByTarget(target) {
this.eventMap.forEach((listeners, event) => {
if(listeners.isEmpty()) {
return;
}
if(listeners.isInvoking) {
listeners.cancelByTarget(target);
}
else {
listeners.removeByTarget(target);
}
});
}
removeByEvent(event:Event_Name)
{
const listeners = this.eventMap.get(event);
if(!listeners || listeners.isEmpty()) {
return;
}
if(listeners.isInvoking) {
listeners.cancelAll();
}
else {
listeners.removeAll();
}
}
}
/**事件名称定义*/
export enum Event_Name {
SCENE_CHANGED = "SCENE_CHANGED",
UI_SHOW = "UI_SHOW",
UI_HIDE = "UI_HIDE",
LANGUAGE_CHANGED = "LANGUAGE_CHANGED",
LANGUAGE_USED = "LANGUAGE_USED",
REMOTE_ASSETS_UPDATED = "REMOTE_ASSETS_UPDATED",
CONFIG_UPDATED = "CONFIG_UPDATED",
}

42
event/event_tool.ts Normal file
View File

@@ -0,0 +1,42 @@
import { Event_Name, MyEvnetHandler, event_mgr } from "../event/event_mgr";
export class EventTool
{
private _eventListeners:{event:Event_Name, handler:MyEvnetHandler}[];
constructor()
{
}
protected addEventListener(event:Event_Name, handler:MyEvnetHandler)
{
event_mgr.get_inst().add(event, handler, this);
if(!this._eventListeners) {
this._eventListeners = [];
}
this._eventListeners.push({event, handler});
}
protected removeEventListener(event:Event_Name, handler:MyEvnetHandler)
{
event_mgr.get_inst().remove(event, handler, this);
}
protected fireEvent(event:Event_Name, ...params)
{
event_mgr.get_inst().fire(event, ...params);
}
protected clearEventListeners()
{
const eventListeners = this._eventListeners;
if(eventListeners && eventListeners.length) {
const eventMgr = event_mgr.get_inst();
eventListeners.forEach(listener => {
eventMgr.remove(listener.event, listener.handler, this);
});
}
this._eventListeners = null;
cc.game.targetOff(this);
}
}

View File

@@ -1,104 +1,104 @@
export type LinkListNode<T> = {
key:number;
data:T;
next:LinkListNode<T>;
}
export class LinkList<T>
{
private pool:LinkListNode<T>[];
private _head:LinkListNode<T>;
private _tail:LinkListNode<T>;
constructor()
{
this._head = this._tail = null;
this.pool = [];
}
private spawn_node(key:number, data:T):LinkListNode<T>
{
let node:LinkListNode<T> = this.pool.pop();
if(node)
{
node.key = key;
node.data = data;
node.next = null;
}
else
{
node = {key:key, data:data, next:null};
}
return node;
}
get head():LinkListNode<T>
{
return this._head;
}
get tail():LinkListNode<T>
{
return this._tail;
}
append(key:number, data:T):number
{
let node:LinkListNode<T> = this.spawn_node(key, data);
//将node加到linklist末尾
if(this._tail)
{
this._tail.next = node;
this._tail = node;
}
else
{
this._head = this._tail = node;
}
return node.key;
}
remove(key:number):LinkListNode<T>
{
if(!key)
{
return null;
}
if(!this._head)
{
return null;
}
let prev:LinkListNode<T>;
let curr:LinkListNode<T> = this._head;
while(curr && curr.key != key)
{
prev = curr;
curr = curr.next;
}
//没找到
if(!curr)
{
return null;
}
if(!prev)
{
//curr为头节点(要区分curr是否同时为尾节点)
this._head = curr.next;
if(!curr.next)
{
this._tail = null;
}
}
else
{
//curr非头节点(要区分curr是否为尾节点)
prev.next = curr.next;
if(!curr.next)
{
this._tail = prev;
}
}
this.pool.push(curr);
return curr;
}
export type LinkListNode<T> = {
key:number;
data:T;
next:LinkListNode<T>;
}
export class LinkList<T>
{
private pool:LinkListNode<T>[];
private _head:LinkListNode<T>;
private _tail:LinkListNode<T>;
constructor()
{
this._head = this._tail = null;
this.pool = [];
}
private spawn_node(key:number, data:T):LinkListNode<T>
{
let node:LinkListNode<T> = this.pool.pop();
if(node)
{
node.key = key;
node.data = data;
node.next = null;
}
else
{
node = {key:key, data:data, next:null};
}
return node;
}
get head():LinkListNode<T>
{
return this._head;
}
get tail():LinkListNode<T>
{
return this._tail;
}
append(key:number, data:T):number
{
let node:LinkListNode<T> = this.spawn_node(key, data);
//将node加到linklist末尾
if(this._tail)
{
this._tail.next = node;
this._tail = node;
}
else
{
this._head = this._tail = node;
}
return node.key;
}
remove(key:number):LinkListNode<T>
{
if(!key)
{
return null;
}
if(!this._head)
{
return null;
}
let prev:LinkListNode<T>;
let curr:LinkListNode<T> = this._head;
while(curr && curr.key != key)
{
prev = curr;
curr = curr.next;
}
//没找到
if(!curr)
{
return null;
}
if(!prev)
{
//curr为头节点(要区分curr是否同时为尾节点)
this._head = curr.next;
if(!curr.next)
{
this._tail = null;
}
}
else
{
//curr非头节点(要区分curr是否为尾节点)
prev.next = curr.next;
if(!curr.next)
{
this._tail = prev;
}
}
this.pool.push(curr);
return curr;
}
}

View File

@@ -1,250 +1,452 @@
import {handler, gen_handler} from "../util"
export class loader_mgr
{
private static inst:loader_mgr;
private constructor(){};
static get_inst():loader_mgr
{
if(!loader_mgr.inst)
{
loader_mgr.inst = new loader_mgr();
}
return loader_mgr.inst;
}
/**从远程url下载资源 */
loadExternalAsset(url:string, cb:handler, type?:string)
{
const res = cc.loader.getRes(url);
if(res)
{
// console.log("loadExternalAsset from cache");
cb.exec(res);
return;
}
cc.loader.load(type ? {url, type} : url, (err, res) => {
if(err)
{
cc.warn("loadExternalAsset error", url);
return;
}
cb.exec(res);
});
}
/**从远程url下载资源列表 */
loadExternalAssets(urls:string[], cb:handler, types?:string[])
{
let loaded_res = {};
let unloaded_urls:string[] = [];
urls.forEach(url => {
let res = cc.loader.getRes(url);
if(res)
{
loaded_res[url] = res;
}
else
{
unloaded_urls.push(url);
}
});
if(unloaded_urls.length == 0)
{
cb.exec(loaded_res);
return;
}
const loadTasks = [];
unloaded_urls.forEach((url, i) => {
types ? loadTasks.push({url, type:types[i]}) : loadTasks.push(url);
})
cc.loader.load(loadTasks, (errs, res) => {
cc.info("loadExternalAssets from remote url");
if(errs)
{
cc.warn("loadExternalAssets error", errs);
return;
}
unloaded_urls.forEach(url => {
loaded_res[url] = res.getContent(url);
});
cb.exec(loaded_res);
});
}
/**从resources目录加载rawassetrawaaset是指cc.Texture2D, cc.AudioClip, cc.ParticleAsset*/
loadRawAsset(url:string, cb:handler)
{
let res:any = cc.loader.getRes(url);
if(res)
{
cb.exec(res);
return;
}
cc.loader.loadRes(url, (err:any, res:any):void=>{
if(err)
{
cc.warn("loadRawAsset error", url);
return;
}
cb.exec(res);
});
}
/**从resources目录加载assetasset是指cc.SpriteFrame, cc.AnimationClip, cc.Prefab*/
loadAsset(url:string, cb:handler, asset_type:typeof cc.Asset):void
{
let res:any = cc.loader.getRes(url, asset_type);
if(res)
{
cb.exec(res);
return;
}
cc.loader.loadRes(url, asset_type, (err:any, res:any):void=>{
if(err)
{
cc.warn("loadAsset error", url);
return;
}
cb.exec(res);
});
}
/**从resources目录加载asset/rawasset列表省略资源后缀*/
loadResArray(urls:string[], cb:handler):void
{
let loaded_res:any = {};
let unloaded_urls:string[] = [];
urls.forEach((url:string):void=>{
let res:any = cc.loader.getRes(url);
if(res)
{
loaded_res[url] = res;
}
else
{
unloaded_urls.push(url);
}
});
if(unloaded_urls.length == 0)
{
cb.exec(loaded_res);
return;
}
cc.loader.loadResArray(unloaded_urls, (err:any, res_arr:any[]):void=>{
if(err)
{
cc.warn("loadResArray error", unloaded_urls);
return;
}
unloaded_urls.forEach((url:string):void=>{
loaded_res[url] = cc.loader.getRes(url);
});
cb.exec(loaded_res);
});
}
/**从resources目录加载prefab(省略资源后缀)加载成功后生成prefab实例*/
loadPrefabObj(url:string, cb:handler)
{
let res:any = cc.loader.getRes(url, cc.Prefab);
if(res)
{
let node:cc.Node = cc.instantiate(res);
cb.exec(node);
return;
}
//err is typeof Error, err.message
cc.loader.loadRes(url, cc.Prefab, (err:any, res:any):void=>{
if(err)
{
cc.warn("loadPrefabObj error", url);
return;
}
let node:cc.Node = cc.instantiate(res);
cb.exec(node);
});
}
/**从resources目录加载prefab列表(省略资源后缀)加载成功后生成prefab实例*/
loadPrefabObjArray(urls:string[], cb:handler):void
{
let loaded_obj:any = {};
let unloaded_urls:string[] = [];
urls.forEach((url:string):void=>{
let res:any = cc.loader.getRes(url, cc.Prefab);
if(res)
{
loaded_obj[url] = cc.instantiate(res);
}
else
{
unloaded_urls.push(url);
}
});
if(unloaded_urls.length == 0)
{
cb.exec(loaded_obj);
return;
}
cc.loader.loadResArray(unloaded_urls, cc.Prefab, (err:any, res_arr:any[]):void=>{
if(err)
{
cc.warn("loadPrefabObjArray error", unloaded_urls);
return;
}
unloaded_urls.forEach((url:string):void=>{
loaded_obj[url] = cc.instantiate(cc.loader.getRes(url, cc.Prefab));
});
cb.exec(loaded_obj);
});
}
loadPrefabDir(dir_path:string, cb:handler):void
{
let map:any = {};
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]):void=>{
if(err)
{
cc.warn("loadPrefabObjDir error", dir_path);
return;
}
urls.forEach((url) => {
map[url] = cc.loader.getRes(url, cc.Prefab);
});
cb.exec(map);
});
}
loadPrefabObjDir(dir_path:string, cb:handler):void
{
let map:any = {};
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]):void=>{
if(err)
{
cc.warn("loadPrefabObjDir error", dir_path);
return;
}
urls.forEach((url) => {
map[url] = cc.instantiate(cc.loader.getRes(url, cc.Prefab));
});
cb.exec(map);
});
}
release(urlOrAssetOrNode:any):void
{
if(urlOrAssetOrNode instanceof cc.Node)
{
//释放节点,从场景上移除
urlOrAssetOrNode.destroy();
}
else
{
//释放缓存引用和资源内容
cc.loader.release(urlOrAssetOrNode);
}
}
import {handler, gen_handler} from "../util"
export class loader_mgr
{
private static inst:loader_mgr;
private _loadedRes:Map<string, boolean>;
private _loadedExternalUrls:Map<string, boolean>;
private constructor()
{
this._loadedRes = new Map();
this._loadedExternalUrls = new Map();
}
static get_inst():loader_mgr
{
if(!this.inst)
{
this.inst = new loader_mgr();
}
return this.inst;
}
setExternalSprite(sprite:cc.Sprite, url:string, reActive = false)
{
this.loadExternalAsset(url, gen_handler((tex:cc.Texture2D) => {
if(!sprite || !cc.isValid(sprite.node))
{
cc.loader.release(url);
return;
}
if(reActive)
{
sprite.node.active = true;
}
sprite.spriteFrame = new cc.SpriteFrame(tex);
}));
}
setExternalSpriteFrame(sprite:cc.Sprite, frame:cc.SpriteFrame, url:string, reActive = false)
{
this.loadExternalAsset(url, gen_handler((tex:cc.Texture2D) => {
if(!sprite || !cc.isValid(sprite.node))
{
cc.loader.release(url);
return;
}
if(reActive)
{
sprite.node.active = true;
}
if(cc.isValid(frame))
{
frame.setTexture(tex);
sprite.spriteFrame = frame;
}
}));
}
setSprite(sprite:cc.Sprite, url:string, reActive = false)
{
this.loadRes(url, gen_handler((tex:cc.Texture2D) => {
if(!sprite || !cc.isValid(sprite.node))
{
cc.loader.release(url);
return;
}
if(reActive)
{
sprite.node.active = true;
}
sprite.spriteFrame = new cc.SpriteFrame(tex);
}));
}
setSpriteFrame(sprite:cc.Sprite, frame:cc.SpriteFrame, url:string, reActive = false)
{
this.loadRes(url, gen_handler((tex:cc.Texture2D) => {
if(!sprite || !cc.isValid(sprite.node))
{
cc.loader.release(url);
return;
}
if(reActive)
{
sprite.node.active = true;
}
if(cc.isValid(frame))
{
frame.setTexture(tex);
sprite.spriteFrame = frame;
}
}));
}
setAtlasSprite(sprite:cc.Sprite, atlasUrl:string, spriteFrameName:string, reActive = false)
{
this.loadRes(atlasUrl, gen_handler((atlas:cc.SpriteAtlas) => {
if(!sprite || !cc.isValid(sprite.node))
{
cc.loader.release(atlasUrl);
return;
}
if(reActive)
{
sprite.node.active = true;
}
sprite.spriteFrame = atlas.getSpriteFrame(spriteFrameName);
}), cc.SpriteAtlas);
}
/**从远程url下载资源 */
loadExternalAsset(url:string, cb:handler, type?:string)
{
const res = cc.loader.getRes(url);
if(cc.isValid(res))
{
// console.log("loadExternalAsset from cache");
cb.exec(res);
return;
}
cc.loader.load(type ? {url, type} : url, (err, res) => {
if(err)
{
cc.warn("loadExternalAsset error", url);
return;
}
if(cc.isValid(res)) {
this.cacheExternalAsset(url);
cb.exec(res);
}
});
}
/**从远程url下载资源列表 */
loadExternalAssets(urls:string[], cb:handler, types?:string[])
{
let loaded_res = {};
let unloaded_urls:string[] = [];
urls.forEach(url => {
let res = cc.loader.getRes(url);
if(cc.isValid(res))
{
loaded_res[url] = res;
}
else
{
unloaded_urls.push(url);
}
});
if(unloaded_urls.length == 0)
{
cb.exec(loaded_res);
return;
}
const loadTasks = [];
unloaded_urls.forEach((url, i) => {
types ? loadTasks.push({url, type:types[i]}) : loadTasks.push(url);
})
cc.loader.load(loadTasks, (errs, ress) => {
// cc.log("loadExternalAssets from remote url");
if(errs)
{
cc.warn("loadExternalAssets error", errs);
return;
}
let isValid = true;
unloaded_urls.forEach(url => {
const res = ress.getContent(url);
if(!cc.isValid(res)) {
isValid = false;
return;
}
loaded_res[url] = res;
this.cacheExternalAsset(url);
});
if(isValid) {
cb.exec(loaded_res);
}
});
}
/**从resources目录加载asset*/
loadRes(url:string, cb:handler, assetType?:typeof cc.Asset):void
{
let res = cc.loader.getRes(url, assetType);
if(cc.isValid(res))
{
cb.exec(res);
return;
}
cc.loader.loadRes(url, assetType, (err, res) => {
if(err)
{
cc.warn("loadAsset error", url);
return;
}
if(cc.isValid(res)) {
this.cacheAsset(res);
cb.exec(res);
}
});
}
/**从resources目录加载asset列表省略资源后缀*/
loadResArray(urls:string[], cb:handler, assetTypes?:typeof cc.Asset[], alias?:string[])
{
//加载同名资源时,需要手动给出不同的别名
let loaded_res = {};
let unloaded_urls:string[] = [];
let unloaded_alias:string[];
let unloaded_types:typeof cc.Asset[];
if(alias)
{
unloaded_alias = [];
}
if(assetTypes)
{
unloaded_types = [];
}
urls.forEach((url, idx) => {
const resType = assetTypes ? assetTypes[idx] : null;
const resAlias = alias ? alias[idx] : null;
const res = cc.loader.getRes(url, resType);
if(cc.isValid(res))
{
loaded_res[resAlias || url] = res;
}
else
{
unloaded_urls.push(url);
if(resType)
{
unloaded_types.push(resType);
}
if(resAlias)
{
unloaded_alias.push(resAlias);
}
}
});
if(unloaded_urls.length == 0)
{
cb.exec(loaded_res);
return;
}
cc.loader.loadResArray(unloaded_urls, unloaded_types, err => {
if(err)
{
cc.warn("loadResArray error", unloaded_urls);
return;
}
let isValid = true;
unloaded_urls.forEach((url, idx) => {
const resType = unloaded_types ? unloaded_types[idx] : null;
const resAlias = unloaded_alias ? unloaded_alias[idx] : null;
const res = cc.loader.getRes(url, resType);
if(!cc.isValid(res)) {
isValid = false;
return;
}
loaded_res[resAlias || url] = res;
this.cacheAsset(res);
});
if(isValid) {
cb.exec(loaded_res);
}
});
}
/**从resources目录加载prefab(省略资源后缀)加载成功后生成prefab实例*/
loadPrefabObj(url:string, cb:handler)
{
let res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
if(cc.isValid(res))
{
let node:cc.Node = cc.instantiate(res);
cb.exec(node);
return;
}
//err is typeof Error, err.message
cc.loader.loadRes(url, cc.Prefab, (err, res:cc.Prefab) => {
if(err)
{
cc.warn("loadPrefabObj error", url);
return;
}
if(cc.isValid(res)) {
this.cacheAsset(res);
let node:cc.Node = cc.instantiate(res);
cb.exec(node);
}
});
}
/**从resources目录加载prefab列表(省略资源后缀)加载成功后生成prefab实例*/
loadPrefabObjArray(urls:string[], cb:handler)
{
let loaded_obj:any = {};
let unloaded_urls:string[] = [];
urls.forEach((url:string) => {
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
if(cc.isValid(res))
{
loaded_obj[url] = cc.instantiate(res);
}
else
{
unloaded_urls.push(url);
}
});
if(unloaded_urls.length == 0)
{
cb.exec(loaded_obj);
return;
}
cc.loader.loadResArray(unloaded_urls, cc.Prefab, err => {
if(err)
{
cc.warn("loadPrefabObjArray error", unloaded_urls);
return;
}
let isValid = true;
unloaded_urls.forEach(url => {
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
if(!cc.isValid(res)) {
isValid = false;
return;
}
loaded_obj[url] = cc.instantiate(res);
this.cacheAsset(res);
});
if(isValid) {
cb.exec(loaded_obj);
}
});
}
loadPrefabDir(dir_path:string, cb:handler)
{
let map:any = {};
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]) => {
if(err)
{
cc.warn("loadPrefabObjDir error", dir_path);
return;
}
let isValid = true;
urls.forEach(url => {
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
if(!cc.isValid(res)) {
isValid = false;
return;
}
map[url] = res;
this.cacheAsset(res);
});
if(isValid) {
cb.exec(map);
}
});
}
loadPrefabObjDir(dir_path:string, cb:handler):void
{
let map:any = {};
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]) => {
if(err)
{
cc.warn("loadPrefabObjDir error", dir_path);
return;
}
let isValid = true;
urls.forEach(url => {
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
if(!cc.isValid(res)) {
isValid = false;
return;
}
map[url] = cc.instantiate(res);
this.cacheAsset(res);
});
if(isValid) {
cb.exec(map);
}
});
}
private cacheAsset(asset:cc.Asset)
{
if(cc.isValid(asset)) {
const key:string = cc.loader._getReferenceKey(asset);
if(key) {
this._loadedRes.set(key, true);
}
}
}
private cacheExternalAsset(url:string)
{
this._loadedExternalUrls.set(url, true);
}
releaseAll(excludeMap = null)
{
const leftRes:Map<string, boolean> = new Map();
this._loadedRes.forEach((_, res) => {
const deps = cc.loader.getDependsRecursively(res);
deps.forEach(d => {
if(!d) {
return;
}
// cc.log(`loaderMgr release loadedRes, dep=${d}, exclude=${excludeMap ? excludeMap[d] : false}`);
if(!excludeMap || !excludeMap[d]) {
cc.loader.release(d);
}
else {
leftRes.set(d, true);
}
});
});
this._loadedRes.clear();
leftRes.forEach((_, d) => {
this._loadedRes.set(d, true);
});
//释放外部资源
this._loadedExternalUrls.forEach((_, url) => {
// cc.log(`loaderMgr release loadedExternalRes, url=${url}`);
cc.loader.release(url);
});
this._loadedExternalUrls.clear();
cc.sys.garbageCollect();
// this.dump();
}
dump()
{
cc.log(`---------------------loader_mgr dump begin--------------------------`);
const cache = cc.loader._cache;
let count = 0;
for(let id in cache)
{
count++;
cc.log(`id=${id}, value=${cache[id]}`);
}
cc.log(`---------------------loader_mgr dump end, totalCount=${count}--------------------------`);
}
}

46
loader/loading_queue.ts Normal file
View File

@@ -0,0 +1,46 @@
import { handler, gen_handler } from "../util";
import { loader_mgr } from "./loader_mgr";
export class LoadingQueue
{
private static _inst:LoadingQueue;
private _loadingMap:Map<string, handler[]>;
private constructor()
{
this._loadingMap = new Map();
}
static getInst()
{
if(!this._inst) {
this._inst = new LoadingQueue();
}
return this._inst;
}
loadPrefabObj(path:string, cb:handler)
{
let cbs = this._loadingMap.get(path);
if(!cbs) {
cbs = [];
this._loadingMap.set(path, cbs);
}
cbs.push(cb);
if(cbs.length > 1) {
return;
}
loader_mgr.get_inst().loadPrefabObj(path, gen_handler((node:cc.Node) => {
const cbs = this._loadingMap.get(path);
if(cbs) {
cbs.forEach(cb => cb.exec(node));
cbs.length = 0;
this._loadingMap.delete(path);
}
}));
}
clear()
{
this._loadingMap.clear();
}
}

105
misc/storage.ts Normal file
View File

@@ -0,0 +1,105 @@
export class LocalStorage
{
private static _inst:LocalStorage;
private constructor()
{
}
static getInst():LocalStorage
{
if(!this._inst)
{
this._inst = new LocalStorage();
}
return this._inst;
}
remove(key:string)
{
cc.sys.localStorage.removeItem(key);
}
clear()
{
cc.sys.localStorage.clear();
}
getString(key:string, defaultValue = "")
{
const value = cc.sys.localStorage.getItem(key);
if(value != null) {
return value;
}
return defaultValue;
}
setString(key:string, value:string)
{
cc.sys.localStorage.setItem(key, value);
}
getInt(key:string, defaultValue = 0)
{
const value = cc.sys.localStorage.getItem(key);
if(value != null)
{
return parseInt(value);
}
return defaultValue;
}
setInt(key:string, value:number)
{
cc.sys.localStorage.setItem(key, value.toString());
}
getBool(key:string, defaultValue = false)
{
const value = cc.sys.localStorage.getItem(key);
if(value != null)
{
return parseInt(value) == 1;
}
return defaultValue;
}
setBool(key:string, value:boolean)
{
cc.sys.localStorage.setItem(key, value ? "1" : "0");
}
getFloat(key:string, defaultValue = 0)
{
const value = cc.sys.localStorage.getItem(key);
if(value != null)
{
return parseFloat(value);
}
return defaultValue;
}
setFloat(key:string, value:number)
{
cc.sys.localStorage.setItem(key, value.toString());
}
getJson(key:string, defaultValue = null)
{
const value = cc.sys.localStorage.getItem(key);
if(value != null)
{
return JSON.parse(value);
}
return defaultValue;
}
setJson(key:string, value:object)
{
cc.sys.localStorage.setItem(key, JSON.stringify(value));
}
}
export const LocalStorageKey = {
initialLangId: "__initialLangId__",
};

9
native/deviceutils.ts Normal file
View File

@@ -0,0 +1,9 @@
export const DeviceUtils = {
setKeepScreenOn(value:boolean) {
jsb.Device.setKeepScreenOn(value);
},
vibrate(duration:number = 0.01) {
jsb.Device.vibrate(duration);
}
};

102
native/fileutils.ts Normal file
View File

@@ -0,0 +1,102 @@
export class FileUtils
{
private static _inst:FileUtils;
private constructor()
{
}
static getInst()
{
if(!this._inst)
{
this._inst = new FileUtils();
}
return this._inst;
}
getWritablePath()
{
return jsb.fileUtils.getWritablePath();
}
createDirectory(dirPath:string):boolean
{
return jsb.fileUtils.createDirectory(dirPath);
}
isDirectoryExist(dirPath:string):boolean
{
return jsb.fileUtils.isDirectoryExist(dirPath);
}
removeDirectory(dirPath:string):boolean
{
return jsb.fileUtils.removeDirectory(dirPath);
}
removeFile(filePath:string):boolean
{
return jsb.fileUtils.removeFile(filePath);
}
isFileExist(filename:string):boolean
{
return jsb.fileUtils.isFileExist(filename);
}
getSearchPaths():string[]
{
return jsb.fileUtils.getSearchPaths();
}
setSearchPaths(searchPaths:string[])
{
return jsb.fileUtils.setSearchPaths(searchPaths);
}
addSearchPath(path:string, front = false)
{
const prevSearchPaths = this.getSearchPaths();
if(prevSearchPaths.indexOf(path) != -1) {
return;
}
return jsb.fileUtils.addSearchPath(path, front);
}
getDataFromFile(filename:string)
{
return jsb.fileUtils.getDataFromFile(filename);
}
getStringFromFile(filename:string):string
{
return jsb.fileUtils.getStringFromFile(filename);
}
getJsonFromFile(filename:string)
{
const str = this.getStringFromFile(filename);
if(str)
{
return JSON.parse(str);
}
return null;
}
writeStringToFile(dataStr:string, fullPath:string):boolean
{
return jsb.fileUtils.writeStringToFile(dataStr, fullPath);
}
writeJsonToFile(data:object, fullPath:string)
{
const str = JSON.stringify(data);
return this.writeStringToFile(str, fullPath);
}
unzipFileToDirAsync(zipFileName:string, destPath:string)
{
return jsb.fileUtils.unzipFileToDirAsync(zipFileName, destPath);
}
}

370
native/md5.ts Executable file
View File

@@ -0,0 +1,370 @@
/*
Javascript MD5 library - version 0.6
Coded (2011-2016) by Luigi Galli - the running geek LG@THLG.NL - http://THLG.NL
Thanks for feedback/tips/suggestions/comments: Roberto Viola, Lucien Nel, Gahl Saraf, Saqib Dareshani, Philip Peterson.
The below code is PUBLIC DOMAIN - NO WARRANTY!
Changelog: Version 0.6 - 2016-06-26
** FIXED: processing an array as input could result in the size of the array changing.
- new email/website
Version 0.4 - 2011-06-19
+ added compact version (md5_compact_min.js), this is a slower but smaller version
(more than 4KB lighter before stripping/minification)
+ added preliminary support for Typed Arrays (see:
https://developer.mozilla.org/en/JavaScript_typed_arrays and
http://www.khronos.org/registry/typedarray/specs/latest/)
MD5() now accepts input data as ArrayBuffer, Float32Array, Float64Array,
Int16Array, Int32Array, Int8Array, Uint16Array, Uint32Array or Uint8Array
- moved unit tests to md5_test.js
- minor refactoring
Version 0.3.* - 2011-06-##
- Internal dev versions
Version 0.2 - 2011-05-22
** FIXED: serious integer overflow problems which could cause a wrong MD5 hash being returned
Version 0.1 - 2011
-Initial version
*/
// if (typeof faultylabs === 'undefined') {
// faultylabs = {};
// }
/*
MD5()
Computes the MD5 hash for the given input data
input : data as String - (Assumes Unicode code points are encoded as UTF-8. If you
attempt to digest Unicode strings using other encodings
you will get incorrect results!)
data as array of characters - (Assumes Unicode code points are encoded as UTF-8. If you
attempt to digest Unicode strings using other encodings
you will get incorrect results!)
data as array of bytes (plain javascript array of integer numbers)
data as ArrayBuffer (see: https://developer.mozilla.org/en/JavaScript_typed_arrays)
data as Float32Array, Float64Array, Int16Array, Int32Array, Int8Array, Uint16Array, Uint32Array or Uint8Array (see: https://developer.mozilla.org/en/JavaScript_typed_arrays)
(DataView is not supported yet)
output: MD5 hash (as Hex Uppercase String)
*/
let cache;
export const MD5 = function(data) {
//use cache first
if(cache) {
const cache_ret = cache.get(data);
if(cache_ret) {
return cache_ret;
}
}
// convert number to (unsigned) 32 bit hex, zero filled string
function to_zerofilled_hex(n) {
var t1 = (n >>> 0).toString(16);
return "00000000".substr(0, 8 - t1.length) + t1;
}
// convert array of chars to array of bytes
function chars_to_bytes(ac) {
var retval = [];
for (var i = 0; i < ac.length; i++) {
retval = retval.concat(str_to_bytes(ac[i]));
}
return retval;
}
// convert a 64 bit unsigned number to array of bytes. Little endian
function int64_to_bytes(num) {
var retval = [];
for (var i = 0; i < 8; i++) {
retval.push(num & 0xFF);
num = num >>> 8;
}
return retval;
}
// 32 bit left-rotation
function rol(num, places) {
return ((num << places) & 0xFFFFFFFF) | (num >>> (32 - places));
}
// The 4 MD5 functions
function fF(b, c, d) {
return (b & c) | (~b & d);
}
function fG(b, c, d) {
return (d & b) | (~d & c);
}
function fH(b, c, d) {
return b ^ c ^ d;
}
function fI(b, c, d) {
return c ^ (b | ~d);
}
// pick 4 bytes at specified offset. Little-endian is assumed
function bytes_to_int32(arr, off) {
return (arr[off + 3] << 24) | (arr[off + 2] << 16) | (arr[off + 1] << 8) | (arr[off]);
}
/*
Conver string to array of bytes in UTF-8 encoding
See:
http://www.dangrossman.info/2007/05/25/handling-utf-8-in-javascript-php-and-non-utf8-databases/
http://stackoverflow.com/questions/1240408/reading-bytes-from-a-javascript-string
How about a String.getBytes(<ENCODING>) for Javascript!? Isn't it time to add it?
*/
function str_to_bytes(str) {
var retval = [ ];
for (var i = 0; i < str.length; i++)
if (str.charCodeAt(i) <= 0x7F) {
retval.push(str.charCodeAt(i));
} else {
var tmp = encodeURIComponent(str.charAt(i)).substr(1).split('%')
for (var j = 0; j < tmp.length; j++) {
retval.push(parseInt(tmp[j], 0x10));
}
}
return retval;
}
// convert the 4 32-bit buffers to a 128 bit hex string. (Little-endian is assumed)
function int128le_to_hex(a, b, c, d) {
var ra = "";
var t = 0;
var ta = 0;
for (var i = 3; i >= 0; i--) {
ta = arguments[i];
t = (ta & 0xFF);
ta = ta >>> 8;
t = t << 8;
t = t | (ta & 0xFF);
ta = ta >>> 8;
t = t << 8;
t = t | (ta & 0xFF);
ta = ta >>> 8;
t = t << 8;
t = t | ta;
ra = ra + to_zerofilled_hex(t);
}
return ra;
}
// conversion from typed byte array to plain javascript array
function typed_to_plain(tarr) {
var retval = new Array(tarr.length);
for (var i = 0; i < tarr.length; i++) {
retval[i] = tarr[i];
}
return retval;
}
// check input data type and perform conversions if needed
var databytes = null;
function cloneArray(inpArr) {
return inpArr.slice();
}
// String
var type_mismatch = null;
if (typeof data == 'string') {
// convert string to array bytes
databytes = str_to_bytes(data);
} else if (data.constructor == Array) {
if (data.length === 0) {
// if it's empty, just assume array of bytes
databytes = cloneArray(data);
} else if (typeof data[0] == 'string') {
databytes = chars_to_bytes(data);
} else if (typeof data[0] == 'number') {
databytes = cloneArray(data);
} else {
type_mismatch = typeof data[0];
}
} else if (typeof ArrayBuffer != 'undefined') {
if (data instanceof ArrayBuffer) {
databytes = typed_to_plain(new Uint8Array(data));
} else if ((data instanceof Uint8Array) || (data instanceof Int8Array)) {
databytes = typed_to_plain(data);
} else if ((data instanceof Uint32Array) || (data instanceof Int32Array) ||
(data instanceof Uint16Array) || (data instanceof Int16Array) ||
(data instanceof Float32Array) || (data instanceof Float64Array)
) {
databytes = typed_to_plain(new Uint8Array(data.buffer));
} else {
type_mismatch = typeof data;
}
} else {
type_mismatch = typeof data;
}
if (type_mismatch) {
alert('MD5 type mismatch, cannot process ' + type_mismatch);
}
function _add(n1, n2) {
return 0x0FFFFFFFF & (n1 + n2);
}
const ret = do_digest();
cache = cache || new Map();
cache.set(data, ret);
return ret;
function do_digest() {
// function update partial state for each run
function updateRun(nf, sin32, dw32, b32) {
var temp = d;
d = c;
c = b;
//b = b + rol(a + (nf + (sin32 + dw32)), b32)
b = _add(b,
rol(
_add(a,
_add(nf, _add(sin32, dw32))
), b32
)
);
a = temp;
}
// save original length
var org_len = databytes.length;
// first append the "1" + 7x "0"
databytes.push(0x80);
// determine required amount of padding
var tail = databytes.length % 64
// no room for msg length?
if (tail > 56) {
// pad to next 512 bit block
for (var i = 0; i < (64 - tail); i++) {
databytes.push(0x0);
}
tail = databytes.length % 64;
}
for (i = 0; i < (56 - tail); i++) {
databytes.push(0x0);
}
// message length in bits mod 512 should now be 448
// append 64 bit, little-endian original msg length (in *bits*!)
databytes = databytes.concat(int64_to_bytes(org_len * 8));
// initialize 4x32 bit state
var h0 = 0x67452301;
var h1 = 0xEFCDAB89;
var h2 = 0x98BADCFE;
var h3 = 0x10325476;
// temp buffers
var a = 0, b = 0, c = 0, d = 0;
// Digest message
for (i = 0; i < databytes.length / 64; i++) {
// initialize run
a = h0;
b = h1;
c = h2;
d = h3;
var ptr = i * 64;
// do 64 runs
updateRun(fF(b, c, d), 0xd76aa478, bytes_to_int32(databytes, ptr), 7);
updateRun(fF(b, c, d), 0xe8c7b756, bytes_to_int32(databytes, ptr + 4), 12);
updateRun(fF(b, c, d), 0x242070db, bytes_to_int32(databytes, ptr + 8), 17);
updateRun(fF(b, c, d), 0xc1bdceee, bytes_to_int32(databytes, ptr + 12), 22);
updateRun(fF(b, c, d), 0xf57c0faf, bytes_to_int32(databytes, ptr + 16), 7);
updateRun(fF(b, c, d), 0x4787c62a, bytes_to_int32(databytes, ptr + 20), 12);
updateRun(fF(b, c, d), 0xa8304613, bytes_to_int32(databytes, ptr + 24), 17);
updateRun(fF(b, c, d), 0xfd469501, bytes_to_int32(databytes, ptr + 28), 22);
updateRun(fF(b, c, d), 0x698098d8, bytes_to_int32(databytes, ptr + 32), 7);
updateRun(fF(b, c, d), 0x8b44f7af, bytes_to_int32(databytes, ptr + 36), 12);
updateRun(fF(b, c, d), 0xffff5bb1, bytes_to_int32(databytes, ptr + 40), 17);
updateRun(fF(b, c, d), 0x895cd7be, bytes_to_int32(databytes, ptr + 44), 22);
updateRun(fF(b, c, d), 0x6b901122, bytes_to_int32(databytes, ptr + 48), 7);
updateRun(fF(b, c, d), 0xfd987193, bytes_to_int32(databytes, ptr + 52), 12);
updateRun(fF(b, c, d), 0xa679438e, bytes_to_int32(databytes, ptr + 56), 17);
updateRun(fF(b, c, d), 0x49b40821, bytes_to_int32(databytes, ptr + 60), 22);
updateRun(fG(b, c, d), 0xf61e2562, bytes_to_int32(databytes, ptr + 4), 5);
updateRun(fG(b, c, d), 0xc040b340, bytes_to_int32(databytes, ptr + 24), 9);
updateRun(fG(b, c, d), 0x265e5a51, bytes_to_int32(databytes, ptr + 44), 14);
updateRun(fG(b, c, d), 0xe9b6c7aa, bytes_to_int32(databytes, ptr), 20);
updateRun(fG(b, c, d), 0xd62f105d, bytes_to_int32(databytes, ptr + 20), 5);
updateRun(fG(b, c, d), 0x2441453, bytes_to_int32(databytes, ptr + 40), 9);
updateRun(fG(b, c, d), 0xd8a1e681, bytes_to_int32(databytes, ptr + 60), 14);
updateRun(fG(b, c, d), 0xe7d3fbc8, bytes_to_int32(databytes, ptr + 16), 20);
updateRun(fG(b, c, d), 0x21e1cde6, bytes_to_int32(databytes, ptr + 36), 5);
updateRun(fG(b, c, d), 0xc33707d6, bytes_to_int32(databytes, ptr + 56), 9);
updateRun(fG(b, c, d), 0xf4d50d87, bytes_to_int32(databytes, ptr + 12), 14);
updateRun(fG(b, c, d), 0x455a14ed, bytes_to_int32(databytes, ptr + 32), 20);
updateRun(fG(b, c, d), 0xa9e3e905, bytes_to_int32(databytes, ptr + 52), 5);
updateRun(fG(b, c, d), 0xfcefa3f8, bytes_to_int32(databytes, ptr + 8), 9);
updateRun(fG(b, c, d), 0x676f02d9, bytes_to_int32(databytes, ptr + 28), 14);
updateRun(fG(b, c, d), 0x8d2a4c8a, bytes_to_int32(databytes, ptr + 48), 20);
updateRun(fH(b, c, d), 0xfffa3942, bytes_to_int32(databytes, ptr + 20), 4);
updateRun(fH(b, c, d), 0x8771f681, bytes_to_int32(databytes, ptr + 32), 11);
updateRun(fH(b, c, d), 0x6d9d6122, bytes_to_int32(databytes, ptr + 44), 16);
updateRun(fH(b, c, d), 0xfde5380c, bytes_to_int32(databytes, ptr + 56), 23);
updateRun(fH(b, c, d), 0xa4beea44, bytes_to_int32(databytes, ptr + 4), 4);
updateRun(fH(b, c, d), 0x4bdecfa9, bytes_to_int32(databytes, ptr + 16), 11);
updateRun(fH(b, c, d), 0xf6bb4b60, bytes_to_int32(databytes, ptr + 28), 16);
updateRun(fH(b, c, d), 0xbebfbc70, bytes_to_int32(databytes, ptr + 40), 23);
updateRun(fH(b, c, d), 0x289b7ec6, bytes_to_int32(databytes, ptr + 52), 4);
updateRun(fH(b, c, d), 0xeaa127fa, bytes_to_int32(databytes, ptr), 11);
updateRun(fH(b, c, d), 0xd4ef3085, bytes_to_int32(databytes, ptr + 12), 16);
updateRun(fH(b, c, d), 0x4881d05, bytes_to_int32(databytes, ptr + 24), 23);
updateRun(fH(b, c, d), 0xd9d4d039, bytes_to_int32(databytes, ptr + 36), 4);
updateRun(fH(b, c, d), 0xe6db99e5, bytes_to_int32(databytes, ptr + 48), 11);
updateRun(fH(b, c, d), 0x1fa27cf8, bytes_to_int32(databytes, ptr + 60), 16);
updateRun(fH(b, c, d), 0xc4ac5665, bytes_to_int32(databytes, ptr + 8), 23);
updateRun(fI(b, c, d), 0xf4292244, bytes_to_int32(databytes, ptr), 6);
updateRun(fI(b, c, d), 0x432aff97, bytes_to_int32(databytes, ptr + 28), 10);
updateRun(fI(b, c, d), 0xab9423a7, bytes_to_int32(databytes, ptr + 56), 15);
updateRun(fI(b, c, d), 0xfc93a039, bytes_to_int32(databytes, ptr + 20), 21);
updateRun(fI(b, c, d), 0x655b59c3, bytes_to_int32(databytes, ptr + 48), 6);
updateRun(fI(b, c, d), 0x8f0ccc92, bytes_to_int32(databytes, ptr + 12), 10);
updateRun(fI(b, c, d), 0xffeff47d, bytes_to_int32(databytes, ptr + 40), 15);
updateRun(fI(b, c, d), 0x85845dd1, bytes_to_int32(databytes, ptr + 4), 21);
updateRun(fI(b, c, d), 0x6fa87e4f, bytes_to_int32(databytes, ptr + 32), 6);
updateRun(fI(b, c, d), 0xfe2ce6e0, bytes_to_int32(databytes, ptr + 60), 10);
updateRun(fI(b, c, d), 0xa3014314, bytes_to_int32(databytes, ptr + 24), 15);
updateRun(fI(b, c, d), 0x4e0811a1, bytes_to_int32(databytes, ptr + 52), 21);
updateRun(fI(b, c, d), 0xf7537e82, bytes_to_int32(databytes, ptr + 16), 6);
updateRun(fI(b, c, d), 0xbd3af235, bytes_to_int32(databytes, ptr + 44), 10);
updateRun(fI(b, c, d), 0x2ad7d2bb, bytes_to_int32(databytes, ptr + 8), 15);
updateRun(fI(b, c, d), 0xeb86d391, bytes_to_int32(databytes, ptr + 36), 21);
// update buffers
h0 = _add(h0, a);
h1 = _add(h1, b);
h2 = _add(h2, c);
h3 = _add(h3, d);
}
// Done! Convert buffers to 128 bit (LE)
return int128le_to_hex(h3, h2, h1, h0);
}
}
// module.exports = MD5;

192
network/downloader.ts Normal file
View File

@@ -0,0 +1,192 @@
//同个文件只会下载一次
//并发下载数量限制
//失败重试机制
export class Downloader
{
private static _inst:Downloader;
private _jsbDownloaderPool:any[];
private _maxDownloadingCnt:number;
private _downloadingCnt:number;
private _downloadQueue:DownloadItem[]; //等待下载列表
private _downloadedMap:Map<string, DownloadTask>; //已下载资源列表url -> DownloadTask
private _downloadingMap:Map<string, DownloadItem[]>; //下载同一个url的item列表
private constructor()
{
if(!CC_JSB)
{
cc.warn('Downloader is a NATIVE ONLY feature.');
}
this._maxDownloadingCnt = 2;
this._downloadingCnt = 0;
}
static getInst()
{
if(!this._inst)
{
this._inst = new Downloader();
}
return this._inst;
}
downloadFile(item:DownloadItem)
{
const requestURL = item.requestURL;
//之前下载过
const task = this._downloadedMap ? this._downloadedMap.get(requestURL) : null;
if(task && item.onFileTaskSuccess)
{
item.onFileTaskSuccess(task);
this.downloadQueueItem();
return;
}
//大于最大并发数量需要等待当前item下载完成
if(this._downloadingCnt >= this._maxDownloadingCnt)
{
this._downloadQueue = this._downloadQueue || [];
this._downloadQueue.push(item);
return;
}
//同一url只下载一次
this._downloadingMap = this._downloadingMap || new Map();
let downloadingItems = this._downloadingMap.get(requestURL);
if(!downloadingItems)
{
downloadingItems = [];
this._downloadingMap.set(requestURL, downloadingItems);
}
downloadingItems.push(item);
if(downloadingItems.length > 1)
{
return;
}
//创建下载任务
let jsbDownloader = this.popJsbDownloader();
jsbDownloader.setOnTaskProgress(this.onTaskProgress.bind(this, requestURL));
jsbDownloader.setOnFileTaskSuccess(this.onFileTaskSuccess.bind(this, requestURL, jsbDownloader));
jsbDownloader.setOnTaskError(this.onTaskError.bind(this, requestURL, jsbDownloader));
jsbDownloader.createDownloadFileTask(requestURL, item.storagePath);
this._downloadingCnt++;
cc.log(`Downloader, downloadFile, url=${requestURL}, downloadingCnt=${this._downloadingCnt}`);
}
purge()
{
if(this._jsbDownloaderPool)
{
this._jsbDownloaderPool.length = 0;
}
}
private downloadQueueItem()
{
if(!this._downloadQueue || this._downloadQueue.length < 1)
{
cc.log(`Downloader, no queue or all queue item had been downloaded`);
return;
}
const item = this._downloadQueue.pop();
this.downloadFile(item);
}
private onTaskProgress(requestURL:string, task:DownloadTask, bytesReceived:number, totalBytesReceived:number, totalBytesExpected:number)
{
if(this._downloadingMap)
{
const downloadingItems = this._downloadingMap.get(requestURL);
downloadingItems.forEach(item => {
if(item.onTaskProgress) {
item.onTaskProgress(task, bytesReceived, totalBytesReceived, totalBytesExpected);
}
});
}
}
private onFileTaskSuccess(requestURL:string, jsbDownloader, task:DownloadTask)
{
//保存下载后的地址
this._downloadedMap = this._downloadedMap || new Map();
this._downloadedMap.set(requestURL, task);
//通知downloadItem下载完成
if(this._downloadingMap)
{
const downloadingItems = this._downloadingMap.get(requestURL);
downloadingItems.forEach(item => {
if(item.onFileTaskSuccess) {
item.onFileTaskSuccess(task);
}
});
downloadingItems.length = 0;
this._downloadingMap.delete(requestURL);
}
this.pushJsbDownloader(jsbDownloader);
this._downloadingCnt--;
cc.log(`Downloader, onFileTaskSuccess, url=${requestURL}, downloadingCnt=${this._downloadingCnt}`);
this.downloadQueueItem();
}
private onTaskError(requestURL:string, jsbDownloader, task:DownloadTask, errCode:number, errCodeInternal:number, errStr:string)
{
if(this._downloadingMap)
{
const downloadingItems = this._downloadingMap.get(requestURL);
downloadingItems.forEach(item => {
if(item.onTaskError) {
item.onTaskError(task, errCode, errStr);
}
});
downloadingItems.length = 0;
this._downloadingMap.delete(requestURL);
}
this.pushJsbDownloader(jsbDownloader);
this._downloadingCnt--;
cc.log(`Downloader, onTaskError, url=${requestURL}, downloadingCnt=${this._downloadingCnt}`);
this.downloadQueueItem();
}
private popJsbDownloader()
{
let jsbDownloader = null;
if(this._jsbDownloaderPool)
{
jsbDownloader = this._jsbDownloaderPool.pop();
}
if(!jsbDownloader)
{
jsbDownloader = new jsb.Downloader();
}
return jsbDownloader;
}
private pushJsbDownloader(jsbDownloader)
{
jsbDownloader.setOnTaskProgress(null);
jsbDownloader.setOnFileTaskSuccess(null);
jsbDownloader.setOnTaskError(null);
this._jsbDownloaderPool = this._jsbDownloaderPool || [];
this._jsbDownloaderPool.push(jsbDownloader);
}
}
interface DownloadItem
{
requestURL:string;
storagePath:string;
onTaskProgress?:(task:DownloadTask, bytesReceived:number, totalBytesReceived:number, totalBytesExpected:number) => void;
onTaskError?:(task:DownloadTask, errCode:number, errStr:string) => void;
onFileTaskSuccess?:(task:DownloadTask) => void;
}
export interface DownloadTask
{
requestURL:string;
storagePath:string;
}

153
network/httpservice.ts Normal file
View File

@@ -0,0 +1,153 @@
import { handler } from "../util";
export class HttpService
{
private static _inst:HttpService;
private constructor()
{
}
static getInst():HttpService
{
if(!this._inst)
{
this._inst = new HttpService();
}
return this._inst;
}
doGet(url:string, headers, params, cb:handler)
{
if(params)
{
if(url.indexOf("?") == -1)
{
url += "?";
}
url += this.getQueryString(params);
}
this.doHttp(url, headers, null, "GET", cb);
}
doPost(url:string, headers, params, cb:handler)
{
this.doHttp(url, headers, params, "POST", cb);
}
doDownload()
{
}
private doHttp(url:string, headers, params, method:string, cb:handler)
{
const xhr = new XMLHttpRequest();
xhr.responseType = "text";
xhr.onreadystatechange = this.onReadyStateChange.bind(this, xhr, cb);
xhr.ontimeout = this.onTimeout.bind(this, xhr, url);
xhr.onerror = this.onError.bind(this, xhr, url);
xhr.onabort = this.onAbort.bind(this, xhr, url);
cc.log(`HttpService, doHttp url=${url}, method=${method}, parmas=${params}`)
xhr.open(method, url, true);
if(headers)
{
this.setHttpHeaders(xhr, headers);
}
if (cc.sys.isNative)
{
this.setHttpHeaders(xhr, {"Accept-Encoding": "gzip,deflate"});
}
if(params && typeof params === "object")
{
params = JSON.stringify(params);
}
xhr.send(params);
}
private onReadyStateChange(xhr:XMLHttpRequest, cb:handler)
{
cc.log(`HttpService, onReadyStateChange, readyState=${xhr.readyState}, status=${xhr.status}`);
if(xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300)
{
cc.log(`HttpService, onReadyStateChange, responseText=${xhr.responseText}`);
let data;
let code = HttpCode.kUnknown;
const response = JSON.parse(xhr.responseText);
if(response && response.code)
{
code = response.code;
data = response.content;
}
else
{
code = HttpCode.kSuccess;
data = response;
}
this.notifyCallback(cb, code, data);
this.removeXhrEvent(xhr);
}
}
private onTimeout(xhr:XMLHttpRequest, url:string)
{
cc.warn(`${url}, request ontimeout`);
this.removeXhrEvent(xhr);
}
private onError(xhr:XMLHttpRequest, url:string)
{
cc.warn(`${url}, request onerror`);
this.removeXhrEvent(xhr);
}
private onAbort(xhr:XMLHttpRequest, url:string)
{
cc.warn(`${url}, request onabort`);
this.removeXhrEvent(xhr);
}
private removeXhrEvent(xhr:XMLHttpRequest)
{
xhr.ontimeout = null;
xhr.onerror = null;
xhr.onabort = null;
xhr.onreadystatechange = null;
}
private notifyCallback(cb:handler, code:number, data?)
{
if(cb)
{
cb.exec(code, data);
}
}
private setHttpHeaders(xhr:XMLHttpRequest, headers)
{
for(let key in headers)
{
xhr.setRequestHeader(key, headers[key]);
}
}
private getQueryString(params)
{
const tmps:string[] = [];
for(let key in params)
{
tmps.push(`${key}=${params[key]}`);
}
return tmps.join("&");
}
}
export enum HttpCode {
kSuccess = 0,
kTimeout = 10000,
kUnknown = 10001,
kSessionTimeout = -8,
kIAmInBlocklist = -3013,
kUserIsInMyBlocklist = -3014
}

View File

@@ -1,61 +1,61 @@
import {loader_mgr} from "../loader/loader_mgr"
import {handler, gen_handler} from "../util"
import {ui_pool} from "./ui_pool"
export class pool_mgr
{
private static _inst:pool_mgr;
private ui_pool:ui_pool;
private constructor()
{
this.ui_pool = new ui_pool();
}
static get_inst():pool_mgr
{
if(!this._inst)
{
this._inst = new pool_mgr();
}
return this._inst;
}
get_ui(path:string, cb:handler):void
{
let ui:cc.Node = this.ui_pool.get(path);
if(ui)
{
// cc.info("pool_mgr:get_ui from cache", path);
cb.exec(ui);
return;
}
// cc.info("pool_mgr:get_ui from loader", path);
loader_mgr.get_inst().loadPrefabObj(path, cb);
}
put_ui(path:string, ui:cc.Node):void
{
if(!ui)
{
cc.warn("pool_mgr:put_ui, invalid node");
return;
}
this.ui_pool.put(path, ui);
}
clear_atpath(path:string)
{
this.ui_pool.clear_atpath(path);
}
clear()
{
this.ui_pool.clear();
}
dump()
{
this.ui_pool.dump();
}
import {loader_mgr} from "../loader/loader_mgr"
import {handler} from "../util"
import {ui_pool} from "./ui_pool"
export class pool_mgr
{
private static inst:pool_mgr;
private ui_pool:ui_pool;
private constructor()
{
this.ui_pool = new ui_pool();
}
static get_inst()
{
if(!this.inst)
{
this.inst = new pool_mgr();
}
return this.inst;
}
get_ui(path:string, cb:handler)
{
const ui:cc.Node = this.ui_pool.get(path);
if(cc.isValid(ui))
{
// cc.log("pool_mgr:get_ui from cache", path);
cb.exec(ui);
return;
}
// cc.log("pool_mgr:get_ui from loader", path);
loader_mgr.get_inst().loadPrefabObj(path, cb);
}
put_ui(path:string, ui:cc.Node, destroyAtOnce = false)
{
if(!cc.isValid(ui))
{
cc.warn("pool_mgr:put_ui, invalid node");
return;
}
this.ui_pool.put(path, ui, destroyAtOnce);
}
clear_atpath(path:string)
{
this.ui_pool.clear_atpath(path);
}
clear()
{
this.ui_pool.clear();
}
dump()
{
this.ui_pool.dump();
}
}

View File

@@ -1,102 +1,103 @@
//lru(last recently used) cache
export class ui_pool
{
private cache:any; //path => cc.Node[]
private path2time:any;
private size:number;
private max_size:number = 2;
constructor()
{
this.cache = {};
this.path2time = {};
this.size = 0;
}
get(path:string):cc.Node
{
let uis:cc.Node[] = this.cache[path];
if(uis && uis.length > 0)
{
this.size--;
return uis.pop();
}
return null;
}
put(path:string, ui:cc.Node):void
{
if(this.size >= this.max_size)
{
//删除最早的缓存
let del_path:string;
let ts:number = cc.sys.now();
for(let p in this.cache)
{
if(this.cache[p].length > 0 && this.path2time[p] < ts)
{
ts = this.path2time[p];
del_path = p;
}
}
if(del_path && del_path != "")
{
let del_ui:cc.Node = this.cache[del_path].pop();
del_ui.destroy();
this.size--;
// cc.info("ui_pool:pool capacity is max, destroy old ui,", del_path);
}
}
let uis:cc.Node[] = this.cache[path];
if(!uis)
{
this.cache[path] = uis = [];
}
ui.removeFromParent(false);
uis.push(ui);
this.size++;
this.path2time[path] = cc.sys.now();
}
clear_atpath(path:string):void
{
let uis:cc.Node[] = this.cache[path];
if(!uis || uis.length <= 0)
{
return;
}
while(uis.length > 0)
{
let ui:cc.Node = uis.pop();
ui.destroy();
this.size--;
}
}
clear():void
{
for(let path in this.cache)
{
this.clear_atpath(path);
}
this.cache = {};
this.path2time = {};
if(this.size != 0)
{
cc.warn("size should be 0, but now is", this.size);
}
}
dump()
{
let str:string = "********ui_pool dump********";
for(let path in this.cache)
{
str += "\n" + path + "\n";
this.cache[path].forEach((u:cc.Node):void=>{
str += u.name + ",";
});
}
cc.info(str);
}
const DestroyAtOnce = false;
//lru(last recently used) cache
export class ui_pool
{
private path2nodes:Map<string, cc.Node[]>; //path => cc.Node[]
private path2time:Map<string, number>;
private size:number;
private max_size = 2;
constructor()
{
this.path2nodes = new Map();
this.path2time = new Map();
this.size = 0;
}
get(path:string)
{
const nodes = this.path2nodes.get(path);
if(nodes && nodes.length > 0)
{
this.size--;
return nodes.pop();
}
return null;
}
put(path:string, node:cc.Node, destroyAtOnce:boolean)
{
if(DestroyAtOnce || destroyAtOnce)
{
node.destroy();
return;
}
if(this.size >= this.max_size)
{
//删除最早的缓存
let del_path:string;
let ts = cc.sys.now();
this.path2nodes.forEach((nodes, p) => {
if(nodes.length > 0 && this.path2time.get(p) < ts)
{
ts = this.path2time.get(p);
del_path = p;
}
});
if(del_path)
{
const del_node = this.path2nodes.get(del_path).pop();
del_node.destroy();
this.size--;
cc.log("ui_pool:pool capacity is max, destroy old ui,", del_path);
}
}
let nodes = this.path2nodes.get(path);
if(!nodes)
{
nodes = [];
this.path2nodes.set(path, nodes);
}
node.removeFromParent(false);
nodes.push(node);
this.size++;
this.path2time.set(path, cc.sys.now());
}
clear_atpath(path:string):void
{
const nodes = this.path2nodes.get(path);
while(nodes && nodes.length > 0)
{
const node = nodes.pop();
node.destroy();
this.size--;
}
}
clear():void
{
this.path2nodes.forEach((_, path) => {
this.clear_atpath(path);
});
this.path2nodes.clear();
this.path2time.clear();
if(this.size != 0)
{
cc.warn("size should be 0, but now is", this.size);
}
}
dump()
{
let str:string = "********ui_pool dump********";
this.path2nodes.forEach((nodes, path) => {
str += "\n" + path + "\n";
nodes.forEach(node => {
str += node.name + ",";
});
});
cc.log(str);
}
}

View File

@@ -1,175 +1,196 @@
import {handler, gen_handler} from "../util"
import {LinkList, LinkListNode} from "../linklist"
export class TimerMgr
{
private static inst:TimerMgr;
private iterList:LinkList<TimerHandler>;
private pendingList:LinkList<TimerHandler>;
private pool:TimerHandler[];
private key:number;
private constructor()
{
this.key = 0;
this.pool = [];
this.iterList = new LinkList<TimerHandler>();
this.pendingList = new LinkList<TimerHandler>();
}
static getInst():TimerMgr
{
if(!this.inst)
{
this.inst = new TimerMgr();
}
return this.inst;
}
add(interval:number, delay:number, repeat:number, cb:handler, is_updater:boolean = false):number
{
let th = this.pool.pop();
if(th)
{
th.interval = interval;
th.delay = delay;
th.repeat = repeat;
th.elapsed = 0;
th.times = 0;
th.is_updater = is_updater;
th.cb = cb;
}
else
{
th = {interval, delay, repeat, elapsed:0, times:0, is_updater, cb};
}
const key = this.pendingList.append(++this.key, th);
// cc.info(`[TimerMgr]addPending, key=${key}`);
return key;
}
remove(key:number)
{
if(!this.removeIter(key))
{
this.removePending(key);
}
}
private removeIter(key:number)
{
const node = this.iterList.remove(key);
if(node)
{
this.pool.push(node.data);
// cc.info(`[TimerMgr]removeIter, key=${key}`);
return true;
}
return false;
}
private removePending(key:number)
{
const node = this.pendingList.remove(key);
if(node)
{
this.pool.push(node.data);
// cc.info(`[TimerMgr]removePending, key=${key}`);
return true;
}
return false;
}
loop(interval:number, cb:handler):number
{
return this.add(interval, 0, 0, cb);
}
loopTimes(interval:number, repeat:number, cb:handler):number
{
return this.add(interval, 0, repeat, cb);
}
frameLoop(cb:handler):number
{
return this.add(1/24, 0, 0, cb);
}
delayLoop(interval:number, delay:number, cb:handler):number
{
return this.add(interval, delay, 0, cb);
}
once(delay:number, cb:handler):number
{
return this.add(0, delay, 1, cb);
}
add_updater(cb:handler):number
{
return this.add(0, 0, 0, cb, true);
}
update(dt:number)
{
let node = this.iterList.head;
//执行当前帧的定时器
while(node)
{
if(node.data.is_updater)
{
//先保存next引用防止回调函数里回收node导致next被修改
const next = node.next;
node.data.cb.exec(dt);
node = next;
continue;
}
if(node.data.repeat != 0 && node.data.times >= node.data.repeat)
{
const next = node.next;
this.removeIter(node.key);
node = next;
continue;
}
if(node.data.elapsed >= node.data.delay + node.data.interval)
{
//exec回调可能会调用remove函数回收timerHandler.避免操作已回收的对象。
// cc.info(`[TimerMgr]execHandler, key=${node.key}`);
const next = node.next;
node.data.times++;
node.data.elapsed = node.data.delay;
node.data.cb.exec();
node = next;
}
else
{
node.data.elapsed += dt;
node = node.next;
}
}
//添加下一帧的定时器
node = this.pendingList.head;
while(node)
{
const key = node.key;
const th = node.data;
node = node.next;
this.pendingList.remove(key);
this.iterList.append(key, th);
}
}
}
type TimerHandler = {
interval:number; //执行间隔, 只执行一次的定时器值为0单位秒
delay:number; //延时多久执行,单位秒
repeat:number; //要执行多少次0表示无限次
elapsed:number; //已过去的时间
times:number; //已执行次数
is_updater:boolean; //是否每帧调用
cb:handler; //回调函数
import {handler} from "../util"
import {LinkList} from "../linklist"
export class TimerMgr
{
private static inst:TimerMgr;
private iterList:LinkList<TimerHandler>;
private pendingList:LinkList<TimerHandler>;
private pool:TimerHandler[];
private key:number;
private constructor()
{
this.key = 0;
this.pool = [];
this.iterList = new LinkList<TimerHandler>();
this.pendingList = new LinkList<TimerHandler>();
}
static getInst():TimerMgr
{
if(!this.inst) {
this.inst = new TimerMgr();
}
return this.inst;
}
add(interval:number, delay:number, repeat:number, cb:handler, is_updater = false, tag:string = null, target:cc.Node = null):number
{
let th = this.pool.pop();
if(!th) {
th = new TimerHandler();
}
th.init(interval, delay, repeat, 0, 0, is_updater, cb, tag, target);
return this.pendingList.append(++this.key, th);
}
remove(key:number)
{
if(!this.removeIter(key)) {
this.removePending(key);
}
}
private removeIter(key:number)
{
const node = this.iterList.remove(key);
if(node) {
this.pool.push(node.data);
node.data = null;
return true;
}
return false;
}
private removePending(key:number)
{
const node = this.pendingList.remove(key);
if(node) {
this.pool.push(node.data);
node.data = null;
return true;
}
return false;
}
loop(interval:number, cb:handler, tag:string = null, target:cc.Node = null):number
{
return this.add(interval, 0, 0, cb, false, tag, target);
}
loopTimes(interval:number, repeat:number, cb:handler, tag:string = null, target:cc.Node = null):number
{
return this.add(interval, 0, repeat, cb, false, tag, target);
}
frameLoop(cb:handler, tag:string = null, target:cc.Node = null):number
{
return this.add(1/60, 0, 0, cb, false, tag, target);
}
delayLoop(interval:number, delay:number, cb:handler, tag:string = null, target:cc.Node = null):number
{
return this.add(interval, delay, 0, cb, false, tag, target);
}
once(delay:number, cb:handler, tag:string = null, target:cc.Node = null):number
{
return this.add(0, delay, 1, cb, false, tag, target);
}
add_updater(cb:handler, tag:string = null, target:cc.Node = null):number
{
return this.add(0, 0, 0, cb, true, tag, target);
}
update(dt:number)
{
let node = this.iterList.head;
//执行当前帧的定时器
while(node) {
const timerHandler = node.data;
// cc.log(`timer update, key=${timerHandler.tag}`);
//目标已失效
if(!timerHandler || !timerHandler.isValid()) {
const next = node.next;
this.removeIter(node.key);
node = next;
continue;
}
const repeat = timerHandler.repeat;
const delay = timerHandler.delay;
const interval = timerHandler.interval;
const times = timerHandler.times;
const elapsed = timerHandler.elapsed;
const cb = timerHandler.cb;
if(timerHandler.is_updater) {
//先保存next引用防止回调函数里回收node导致next被修改
const next = node.next;
cb.exec(dt);
node = next;
continue;
}
if(repeat != 0 && times >= repeat) {
const next = node.next;
this.removeIter(node.key);
node = next;
continue;
}
if(elapsed >= delay + interval) {
//exec回调可能会调用remove函数回收timerHandler.避免操作已回收的对象。
const next = node.next;
timerHandler.times++;
timerHandler.elapsed = delay;
cb.exec();
node = next;
}
else {
timerHandler.elapsed += dt;
node = node.next;
}
}
//添加下一帧的定时器
node = this.pendingList.head;
while(node) {
const key = node.key;
const th = node.data;
node = node.next;
this.pendingList.remove(key);
this.iterList.append(key, th);
}
}
}
class TimerHandler
{
public interval:number;
public delay:number;
public repeat:number;
public elapsed:number;
public times:number;
public is_updater:boolean;
public cb:handler;
public tag:string;
public target:cc.Node;
private _hasTarget:boolean;
init(interval:number, delay:number, repeat:number, elapsed:number, times:number, is_updater:boolean, cb:handler, tag:string, target:cc.Node)
{
this.interval = interval;
this.delay = delay;
this.repeat = repeat;
this.elapsed = elapsed;
this.times = times;
this.is_updater = is_updater;
this.cb = cb;
this.tag = tag;
this.target = target;
this._hasTarget = target != null;
}
isValid()
{
if(!this._hasTarget) {
return true;
}
return cc.isValid(this.target) && this.target.activeInHierarchy;
}
}

63
tree/nodebase.ts Normal file
View File

@@ -0,0 +1,63 @@
type NodeFinishedCb = (...params) => void;
export class NodeBase
{
private _next:NodeBase;
private _finishedCb:NodeFinishedCb;
constructor()
{
}
protected onEnter(...params)
{
}
protected execute(...params)
{
}
protected onExit(...params)
{
}
start(...params)
{
this.onEnter(...params);
this.execute(...params);
}
protected continue(...params)
{
this.onExit(...params);
if(this._finishedCb)
{
this._finishedCb(...params);
}
if(this._next)
{
this._next.start(...params);
}
}
protected break()
{
this.onExit();
this._next = null;
}
setNext(next:NodeBase)
{
this._next = next;
}
setFinishedCb(cb:NodeFinishedCb)
{
this._finishedCb = cb;
}
protected getName()
{
return "NodeBase";
}
}

46
tree/nodesequence.ts Normal file
View File

@@ -0,0 +1,46 @@
import { NodeBase } from "./nodebase";
//子node依次执行最后执行自身continue
export class NodeSequence extends NodeBase
{
private _nodes:NodeBase[];
constructor(...nodes:NodeBase[])
{
super();
this._nodes = nodes;
this.linkNodes();
}
protected execute()
{
if(this._nodes.length > 0)
{
this._nodes[0].start();
}
}
appendNode(node:NodeBase)
{
this._nodes.push(node);
}
linkNodes()
{
const nodes = this._nodes;
for(let i = 0, len = nodes.length; i < len - 1; i++)
{
nodes[i].setNext(nodes[i + 1]);
nodes[i].setFinishedCb(null);
}
if(nodes.length > 0)
{
nodes[nodes.length - 1].setFinishedCb(this.continue.bind(this));
}
}
protected getName()
{
return "NodeSequence";
}
}

57
tree/nodespawn.ts Normal file
View File

@@ -0,0 +1,57 @@
import { NodeBase } from "./nodebase";
//子node并行执行等待它们全部执行完毕才执行自身continue
export class NodeSpawn extends NodeBase
{
private _nodes:NodeBase[];
private _waitCount:number;
constructor(...nodes:NodeBase[])
{
super()
this._nodes = nodes;
this.arrageNodes();
}
protected execute()
{
this._nodes.forEach(node => {
node.start();
});
}
appendNode(node:NodeBase)
{
this._nodes.push(node);
this.arrageNodes();
}
insertNodes(index:number, ...node:NodeBase[])
{
this._nodes.splice(index, 0, ...node);
this.arrageNodes();
}
private arrageNodes()
{
const nodes = this._nodes;
this._waitCount = nodes.length;
nodes.forEach(node => {
node.setFinishedCb(this.onChildNodeFinished.bind(this));
});
}
private onChildNodeFinished()
{
this._waitCount--;
if(this._waitCount == 0)
{
this.continue();
}
}
protected getName()
{
return "NodeSpawn";
}
}

View File

@@ -1,151 +1,151 @@
/*
t:timestamp,动画执行到当前帧花费的时间
b:begining,起始值
c:change,需要变化的量
d:duration动画的总时间
参见http://www.cnblogs.com/bluedream2009/archive/2010/06/19/1760909.html
*/
export let TweenFunc = {
Linear: function(t,b,c,d){ return c*t/d + b; },
Quad: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t + b;
},
easeOut: function(t,b,c,d){
return -c *(t/=d)*(t-2) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
}
},
Cubic: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t + b;
},
easeOut: function(t,b,c,d){
return c*((t=t/d-1)*t*t + 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
},
Quart: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t*t + b;
},
easeOut: function(t,b,c,d){
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
return -c/2 * ((t-=2)*t*t*t - 2) + b;
}
},
Quint: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t*t*t + b;
},
easeOut: function(t,b,c,d){
return c*((t=t/d-1)*t*t*t*t + 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*t + 2) + b;
}
},
Sine: {
easeIn: function(t,b,c,d){
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
},
easeOut: function(t,b,c,d){
return c * Math.sin(t/d * (Math.PI/2)) + b;
},
easeInOut: function(t,b,c,d){
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}
},
Expo: {
easeIn: function(t,b,c,d){
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
},
easeOut: function(t,b,c,d){
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
},
easeInOut: function(t,b,c,d){
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
},
Circ: {
easeIn: function(t,b,c,d){
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
},
easeOut: function(t,b,c,d){
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
}
},
Elastic: {
easeIn: function(t,b,c,d,a,p){
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
easeOut: function(t,b,c,d,a,p){
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
},
easeInOut: function(t,b,c,d,a,p){
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
}
},
Back: {
easeIn: function(t,b,c,d,s){
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
},
easeOut: function(t,b,c,d,s){
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
easeInOut: function(t,b,c,d,s){
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
}
},
Bounce: {
easeIn: function(t,b,c,d){
return c - TweenFunc.Bounce.easeOut(d-t, 0, c, d) + b;
},
easeOut: function(t,b,c,d){
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
} else {
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
}
},
easeInOut: function(t,b,c,d){
if (t < d/2) return TweenFunc.Bounce.easeIn(t*2, 0, c, d) * .5 + b;
else return TweenFunc.Bounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
}
}
/*
t:timestamp,动画执行到当前帧花费的时间
b:begining,起始值
c:change,需要变化的量
d:duration动画的总时间
参见http://www.cnblogs.com/bluedream2009/archive/2010/06/19/1760909.html
*/
export let TweenFunc = {
Linear: function(t,b,c,d){ return c*t/d + b; },
Quad: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t + b;
},
easeOut: function(t,b,c,d){
return -c *(t/=d)*(t-2) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
}
},
Cubic: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t + b;
},
easeOut: function(t,b,c,d){
return c*((t=t/d-1)*t*t + 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
},
Quart: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t*t + b;
},
easeOut: function(t,b,c,d){
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
return -c/2 * ((t-=2)*t*t*t - 2) + b;
}
},
Quint: {
easeIn: function(t,b,c,d){
return c*(t/=d)*t*t*t*t + b;
},
easeOut: function(t,b,c,d){
return c*((t=t/d-1)*t*t*t*t + 1) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*t + 2) + b;
}
},
Sine: {
easeIn: function(t,b,c,d){
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
},
easeOut: function(t,b,c,d){
return c * Math.sin(t/d * (Math.PI/2)) + b;
},
easeInOut: function(t,b,c,d){
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}
},
Expo: {
easeIn: function(t,b,c,d){
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
},
easeOut: function(t,b,c,d){
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
},
easeInOut: function(t,b,c,d){
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
},
Circ: {
easeIn: function(t,b,c,d){
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
},
easeOut: function(t,b,c,d){
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
},
easeInOut: function(t,b,c,d){
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
}
},
Elastic: {
easeIn: function(t,b,c,d,a,p){
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
easeOut: function(t,b,c,d,a,p){
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
},
easeInOut: function(t,b,c,d,a,p){
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
}
},
Back: {
easeIn: function(t,b,c,d,s){
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
},
easeOut: function(t,b,c,d,s){
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
easeInOut: function(t,b,c,d,s){
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
}
},
Bounce: {
easeIn: function(t,b,c,d){
return c - TweenFunc.Bounce.easeOut(d-t, 0, c, d) + b;
},
easeOut: function(t,b,c,d){
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
} else {
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
}
},
easeInOut: function(t,b,c,d){
if (t < d/2) return TweenFunc.Bounce.easeIn(t*2, 0, c, d) * .5 + b;
else return TweenFunc.Bounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
}
}
}

View File

@@ -1,318 +1,509 @@
import {TimerMgr} from "../timer/timer_mgr"
import {handler, gen_handler} from "../util"
import {LinkList} from "../linklist"
import {TweenFunc} from "./tweenfunc"
export class TweenUtil
{
private static inst:TweenUtil;
private iterList:LinkList<TweenHandler>;
private pendingList:LinkList<TweenHandler>;
private pool:TweenHandler[];
private key:number;
private timer:number;
private constructor()
{
this.key = 0;
this.pool = [];
this.iterList = new LinkList<TweenHandler>();
this.pendingList = new LinkList<TweenHandler>();
}
public static getInst()
{
if(!this.inst)
{
this.inst = new TweenUtil();
}
return this.inst;
}
to(params:TweenParams):number
{
const node = params.node;
if(!node || !cc.isValid(node))
{
cc.warn("invalid node");
return 0;
}
let th = this.pool.pop();
if(!th)
{
th = {
node:null, elapsed:null, duration:null, delay:null,
exectors:null, tweenFunc:null, onUpdate:null, onComplete:null
};
}
th.node = node;
th.elapsed = 0;
th.duration = params.duration || 1;
th.delay = params.delay || 0;
th.exectors = [];
th.tweenFunc = params.tweenFunc || TweenFunc.Linear;
th.onUpdate = params.onUpdate;
th.onComplete = params.onComplete;
if(params.x != null)
{
const from = node.x;
const delta = params.x - from;
th.exectors.push((elapsed) => {
const curr_x = th.tweenFunc(elapsed, from, delta, th.duration);
th.node.x = curr_x; //测试发现用node.position.x不能移动位置
});
}
if(params.y != null)
{
const from = node.y;
const delta = params.y - from;
th.exectors.push((elapsed) => {
const curr_y = th.tweenFunc(elapsed, from, delta, th.duration);
th.node.y = curr_y;
});
}
if(params.rotation != null)
{
const from = node.rotation;
const delta = params.rotation - from;
th.exectors.push((elapsed) => {
const curr_rot = th.tweenFunc(elapsed, from, delta, th.duration);
th.node.rotation = curr_rot;
});
}
if(params.width != null)
{
const from = node.width;
const delta = params.width - from;
th.exectors.push((elapsed) => {
const curr_width = th.tweenFunc(elapsed, from, delta, th.duration);
th.node.width = curr_width;
});
}
if(params.height != null)
{
const from = node.height;
const delta = params.height - from;
th.exectors.push((elapsed) => {
const curr_height = th.tweenFunc(elapsed, from, delta, th.duration);
th.node.height = curr_height;
});
}
if(params.opacity != null)
{
const from = node.opacity;
const delta = params.opacity - from;
th.exectors.push((elapsed) => {
const curr_opacity = th.tweenFunc(elapsed, from, delta, th.duration);
th.node.opacity = curr_opacity;
});
}
if(!this.timer)
{
this.timer = TimerMgr.getInst().add_updater(gen_handler(this.update, this));
}
return this.pendingList.append(++this.key, th);
}
from(params:TweenParams):number
{
const node = params.node;
if(!node || !cc.isValid(node))
{
cc.warn("invalid node");
return 0;
}
if(params.x != null)
{
[node.x, params.x] = [params.x, node.x];
}
if(params.y != null)
{
[node.y, params.y] = [params.y, node.y];
}
if(params.rotation != null)
{
[node.rotation, params.rotation] = [params.rotation, node.rotation];
}
if(params.width != null)
{
[node.width, params.width] = [params.width, node.width];
}
if(params.height != null)
{
[node.height, params.height] = [params.height, node.height];
}
if(params.opacity != null)
{
[node.opacity, params.opacity] = [params.opacity, node.opacity];
}
return this.to(params);
}
kill(key:number)
{
if(!this.killIter(key))
{
this.killPending(key);
}
}
private killIter(key:number)
{
const node = this.iterList.remove(key);
if(node)
{
this.pool.push(node.data);
return true;
}
return false;
}
private killPending(key:number)
{
const node = this.pendingList.remove(key);
if(node)
{
this.pool.push(node.data);
return true;
}
return false;
}
static from(params:TweenParams):number
{
return this.getInst().from(params);
}
static to(params:TweenParams):number
{
return this.getInst().to(params);
}
static kill(key:number)
{
this.getInst().kill(key);
}
private update(dt:number)
{
//什么都没有,停止定时器
if(!this.iterList.head && !this.pendingList.head)
{
TimerMgr.getInst().remove(this.timer);
this.timer = 0;
return;
}
//执行当前帧的th
let node = this.iterList.head;
while(node)
{
//节点已失效
const displayNode = node.data.node;
if(!displayNode || !cc.isValid(displayNode))
{
//先保存next引用防止回调函数里回收node导致next被修改
const next = node.next;
this.killIter(node.key);
node = next;
continue;
}
const elapsed = node.data.elapsed;
const delay = node.data.delay;
const duration = node.data.duration;
//执行完毕
if(elapsed >= duration + delay)
{
const next = node.next;
const key = node.key;
node.data.exectors.forEach(func => {
func(duration);
});
if(node.data.onComplete)
{
node.data.onComplete.exec(displayNode, 1);
}
this.killIter(key);
node = next;
continue;
}
//延时时间到了
if(elapsed >= delay)
{
//onUpdate回调可能会调用kill回收tweenHandler.避免操作已回收的对象。
const next = node.next;
node.data.elapsed += dt;
node.data.exectors.forEach(func => {
func(elapsed - delay);
});
if(node.data.onUpdate)
{
node.data.onUpdate.exec(displayNode, this.clamp01((elapsed - delay) / duration));
}
node = next;
}
else
{
node.data.elapsed += dt;
node = node.next;
}
}
//添加下一帧的th
node = this.pendingList.head;
while(node)
{
const key = node.key;
const th = node.data;
node = node.next;
this.pendingList.remove(key);
this.iterList.append(key, th);
}
}
private clamp01(value:number)
{
if(value < 0)
{
value = 0;
}
if(value > 1)
{
value = 1;
}
return value;
}
}
type TweenHandler = {
node:cc.Node;
elapsed:number;
delay:number;
duration:number;
tweenFunc:Function;
exectors:((elapsed:number) => void)[];
onUpdate?:handler;
onComplete?:handler;
}
type TweenParams = {
node:cc.Node;
duration:number; //动画持续时间,单位秒
delay?:number; //延时多久执行
x?:number;
y?:number;
rotation?:number;
width?:number;
height?:number;
opacity?:number;
tweenFunc?:Function;
onUpdate?:handler;
onComplete?:handler;
}
import {TimerMgr} from "../timer/timer_mgr"
import {gen_handler} from "../util"
import {LinkList} from "../linklist"
import {TweenFunc} from "./tweenfunc"
const clamp01 = (value:number) => {
if(value < 0) {
value = 0;
}
if(value > 1) {
value = 1;
}
return value;
};
export class TweenUtil
{
private static inst:TweenUtil;
private iterList:LinkList<TweenTarget>;
private pendingList:LinkList<TweenTarget>;
private pool:TweenTarget[];
private key:number;
private timer:number;
private constructor()
{
this.key = 0;
this.pool = [];
this.iterList = new LinkList<TweenTarget>();
this.pendingList = new LinkList<TweenTarget>();
}
public static getInst()
{
if(!this.inst) {
this.inst = new TweenUtil();
}
return this.inst;
}
from(params:TweenParams):number
{
const target = params.target;
if(!target || !cc.isValid(target)) {
cc.warn("TweenUtil, invalid tween target");
return 0;
}
let tweenTarget = this.pool.pop();
if(!tweenTarget) {
tweenTarget = new TweenTarget();
}
tweenTarget.init(target, 0, params.delay || 0, params.duration || 1, params.tweenFunc || TweenFunc.Linear, params.onUpdate, params.onComplete);
if(params.x != null) {
const prop = TweenProperty.x;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.x);
params.x = value;
}
if(params.y != null) {
const prop = TweenProperty.y;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.y);
params.y = value;
}
if(params.scaleX != null) {
const prop = TweenProperty.scaleX;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.scaleX);
params.scaleX = value;
}
if(params.scaleY != null) {
const prop = TweenProperty.scaleY;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.scaleY);
params.scaleY = value;
}
if(params.scale != null) {
const prop = TweenProperty.scale;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.scale);
params.scale = value;
}
if(params.rotation != null) {
const prop = TweenProperty.rotation;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.rotation);
params.rotation = value;
}
if(params.width != null) {
const prop = TweenProperty.width;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.width);
params.width = value;
}
if(params.height != null) {
const prop = TweenProperty.height;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.height);
params.height = value;
}
if(params.opacity != null) {
const prop = TweenProperty.opacity;
const value = tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.opacity);
params.opacity = value;
}
if(params.startValue != null) {
const prop = TweenProperty.string;
params.stopValue = params.stopValue || tweenTarget.getValue(prop);
tweenTarget.setValue(prop, params.startValue);
}
return this.to(params, tweenTarget);
}
to(params:TweenParams, tweenTarget:TweenTarget = null):number
{
const target = params.target;
if(!target || !cc.isValid(target)) {
cc.warn("TweenUtil, invalid tween target");
return 0;
}
if(!tweenTarget) {
tweenTarget = this.pool.pop();
if(!tweenTarget) {
tweenTarget = new TweenTarget();
}
tweenTarget.init(target, 0, params.delay || 0, params.duration || 1, params.tweenFunc || TweenFunc.Linear, params.onUpdate, params.onComplete);
}
const funcs:((elapsed:number) => void)[] = [];
if(params.x != null) {
const prop = TweenProperty.x;
const from = tweenTarget.getValue(prop);
const delta = params.x - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.y != null) {
const prop = TweenProperty.y;
const from = tweenTarget.getValue(prop);
const delta = params.y - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.scaleX != null) {
const prop = TweenProperty.scaleX;
const from = tweenTarget.getValue(prop);
const delta = params.scaleX - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.scaleY != null) {
const prop = TweenProperty.scaleY;
const from = tweenTarget.getValue(prop);
const delta = params.scaleY - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.scale != null) {
const prop = TweenProperty.scale;
const from = tweenTarget.getValue(prop);
const delta = params.scale - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.rotation != null) {
const prop = TweenProperty.rotation;
const from = tweenTarget.getValue(prop);
const delta = params.rotation - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.width != null) {
const prop = TweenProperty.width;
const from = tweenTarget.getValue(prop);
const delta = params.width - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.height != null) {
const prop = TweenProperty.height;
const from = tweenTarget.getValue(prop);
const delta = params.height - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.opacity != null) {
const prop = TweenProperty.opacity;
const from = tweenTarget.getValue(prop);
const delta = params.opacity - from;
funcs.push(elapsed => {
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
tweenTarget.setValue(prop, value);
});
}
if(params.stopValue != null) {
const prop = TweenProperty.string;
const from = params.startValue || tweenTarget.getValue(prop);
const delta = params.stopValue - from;
funcs.push(elapsed => {
const value = parseInt(tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration));
if(params.onSetValue && tweenTarget.isValid()) {
params.onSetValue(value);
}
else {
tweenTarget.setValue(prop, value);
}
});
}
const onUpdate = tweenTarget.onUpdate;
const onComplete = tweenTarget.onComplete;
tweenTarget.onUpdate = (ratio:number, elapsed:number) => {
if(!tweenTarget.isValid()) {
return;
}
funcs.forEach(func => func(elapsed));
if(onUpdate) {
onUpdate(ratio, elapsed);
}
};
tweenTarget.onComplete = (ratio:number, elapsed:number) => {
if(!tweenTarget.isValid()) {
return;
}
funcs.forEach(func => func(elapsed));
if(onComplete) {
onComplete(ratio, elapsed);
}
};
this.addTween(tweenTarget);
}
private ratio(duration:number, delay?:number, onUpdate?:TweenOnUpdate, onComplete?:TweenOnComplete, tweenFunc?:Function)
{
let tweenTarget = this.pool.pop();
if(!tweenTarget) {
tweenTarget = new TweenTarget();
}
tweenTarget.init(null, 0, delay || 0, duration || 1, tweenFunc || TweenFunc.Linear, onUpdate, onComplete);
this.addTween(tweenTarget);
}
private addTween(tweenTarget:TweenTarget)
{
if(!this.timer) {
this.timer = TimerMgr.getInst().add_updater(gen_handler(this.update, this), "TweenUtil addTween");
}
return this.pendingList.append(++this.key, tweenTarget);
}
private kill(key:number)
{
if(!this.killIter(key)) {
this.killPending(key);
}
}
private killIter(key:number)
{
const node = this.iterList.remove(key);
if(node) {
this.pool.push(node.data);
node.data = null;
return true;
}
return false;
}
private killPending(key:number)
{
const node = this.pendingList.remove(key);
if(node) {
this.pool.push(node.data);
node.data = null;
return true;
}
return false;
}
static from(params:TweenParams):number
{
return this.getInst().from(params);
}
static to(params:TweenParams):number
{
return this.getInst().to(params);
}
static ratio(duration:number, delay?:number, onUpdate?:TweenOnUpdate, onComplete?:TweenOnComplete, tweenFunc?:Function)
{
return this.getInst().ratio(duration, delay, onUpdate, onComplete, tweenFunc);
}
static kill(key:number)
{
this.getInst().kill(key);
}
private update(dt:number)
{
//什么都没有,停止定时器
if(!this.iterList.head && !this.pendingList.head) {
TimerMgr.getInst().remove(this.timer);
this.timer = 0;
return;
}
//执行当前帧的th
let node = this.iterList.head;
while(node) {
const tweenTarget = node.data;
//目标已失效
if(!tweenTarget || !tweenTarget.isValid()) {
//先保存next引用防止回调函数里回收node导致next被修改
const next = node.next;
this.killIter(node.key);
node = next;
continue;
}
const elapsed = tweenTarget.elapsed;
const delay = tweenTarget.delay;
const duration = tweenTarget.duration;
//执行完毕
if(elapsed >= duration + delay) {
const next = node.next;
const key = node.key;
if(tweenTarget.onUpdate) {
tweenTarget.onUpdate(1, duration);
}
if(tweenTarget.onComplete) {
tweenTarget.onComplete(1, duration);
}
this.killIter(key);
node = next;
continue;
}
//延时时间到了
if(elapsed >= delay) {
//onUpdate回调可能会调用kill回收tweenHandler.避免操作已回收的对象。
const next = node.next;
tweenTarget.elapsed += dt;
if(tweenTarget.onUpdate) {
tweenTarget.onUpdate(clamp01((elapsed - delay) / duration), elapsed - delay);
}
node = next;
}
else {
tweenTarget.elapsed += dt;
node = node.next;
}
}
//添加下一帧的th
node = this.pendingList.head;
while(node) {
const key = node.key;
const tt = node.data;
node = node.next;
this.pendingList.remove(key);
this.iterList.append(key, tt);
}
}
}
interface TweenParams
{
target:cc.Node|cc.Label;
duration:number; //动画持续时间,单位秒
delay?:number; //延时多久执行
x?:number;
y?:number;
scaleX?:number;
scaleY?:number;
scale?:number;
rotation?:number;
width?:number;
height?:number;
opacity?:number;
startValue?:number;
stopValue?:number;
tweenFunc?:Function;
onUpdate?:TweenOnUpdate;
onComplete?:TweenOnComplete;
onSetValue?:TweenOnSetValue;
}
enum TweenProperty
{
x = "x",
y = "y",
scaleX = "scaleX",
scaleY = "scaleY",
scale = "scale",
rotation = "rotation",
width = "width",
height = "height",
opacity = "opacity",
string = "string",
}
type TweenOnUpdate = (ratio:number, elapsed:number) => void;
type TweenOnComplete = (ratio:number, elapsed:number) => void;
type TweenOnSetValue = (value:number) => void;
class TweenTarget
{
public elapsed:number;
public delay:number;
public duration:number;
public tweenFunc:Function;
public onUpdate:TweenOnUpdate;
public onComplete:TweenOnComplete;
private _hasTarget:boolean;
private _node:cc.Node;
private _label:cc.Label;
init(target:cc.Node|cc.Label, elapsed:number, delay:number, duration:number, tweenFunc:Function, onUpdate:TweenOnUpdate, onComplete:TweenOnComplete)
{
this.elapsed = elapsed;
this.delay = delay;
this.duration = duration;
this.tweenFunc = tweenFunc;
this.onUpdate = onUpdate;
this.onComplete = onComplete;
this._hasTarget = target != null;
if(target) {
if((<cc.Node>target).getPosition) {
this._node = target as cc.Node;
this._label = null;
}
else if((<cc.Label>target).string) {
this._label = target as cc.Label;
this._node = null;
}
}
}
isValid()
{
if(!this._hasTarget) {
return true;
}
if(this._node) {
return cc.isValid(this._node);
}
else if(this._label && this._label.node) {
return cc.isValid(this._label.node);
}
return false;
}
setValue(key:TweenProperty, value:number)
{
if(!this._hasTarget) {
return;
}
if(!this.isValid()) {
return;
}
if(this._node) {
this._node[key] = value;
}
else if(this._label && this._label.node) {
this._label[key] = value;
}
}
getValue(key:TweenProperty):number
{
if(!this._hasTarget) {
return 0;
}
if(!this.isValid()) {
return 0;
}
if(this._node) {
return parseFloat(this._node[key]);
}
else if(this._label && this._label.node) {
return parseFloat(this._label[key]);
}
return 0;
}
}

50
ui/base_scene.ts Normal file
View File

@@ -0,0 +1,50 @@
import { BaseUIComponent } from "./base_ui_component";
const {ccclass, property} = cc._decorator;
@ccclass
export class BaseScene extends BaseUIComponent {
private _name:string;
setName(value:string)
{
this._name = value;
}
/**
* 只能由scene_mgr调用
*/
__onStarted__(...params)
{
cc.log(`scene<${this._name}> __onStarted__`, ...params);
this.onStarted(...params);
this.enabled = true;
}
/**
* 只能由scene_mgr调用
*/
__beforeDestroy__()
{
cc.log(`scene<${this._name}> __beforeDestroy__`);
this.enabled = false;
this.clearEventListeners();
this.beforeDestroy();
}
/**
* 场景start方法后调用
*/
onStarted(...params)
{
}
/**
* 场景onDestroy方法前调用
*/
beforeDestroy()
{
}
}

43
ui/base_ui_component.ts Normal file
View File

@@ -0,0 +1,43 @@
import { Event_Name, MyEvnetHandler, event_mgr } from "../event/event_mgr";
export class BaseUIComponent extends cc.Component
{
private _eventListeners:{event:Event_Name, handler:MyEvnetHandler}[];
constructor()
{
super();
}
protected addEventListener(event:Event_Name, handler:MyEvnetHandler)
{
event_mgr.get_inst().add(event, handler, this);
if(!this._eventListeners) {
this._eventListeners = [];
}
this._eventListeners.push({event, handler});
}
protected removeEventListener(event:Event_Name, handler:MyEvnetHandler)
{
event_mgr.get_inst().remove(event, handler, this);
}
protected fireEvent(event:Event_Name, ...params)
{
event_mgr.get_inst().fire(event, ...params);
}
protected clearEventListeners()
{
const eventListeners = this._eventListeners;
if(eventListeners && eventListeners.length) {
const eventMgr = event_mgr.get_inst();
eventListeners.forEach(listener => {
eventMgr.remove(listener.event, listener.handler, this);
});
}
this._eventListeners = null;
cc.game.targetOff(this);
}
}

View File

@@ -1,191 +1,473 @@
import {pool_mgr} from "../pool/pool_mgr"
import {handler, gen_handler} from "../util"
import {POP_UI_BASE} from "./pop_ui_base"
import { TimerMgr } from "../timer/timer_mgr"
import * as utils from '../util'
import { TweenUtil } from "../tween/tweenutil"
import { TweenFunc } from "../tween/tweenfunc"
export class pop_mgr
{
private static inst:pop_mgr;
private ui_cache:any; //path => pop_ui
private ui_stack:Array<string>; //ui stacks
private ui_show_handler:handler;
private ui_hide_handler:handler;
private constructor()
{
this.ui_cache = {};
this.ui_stack = new Array<string>();
}
static get_inst():pop_mgr
{
if(!this.inst)
{
this.inst = new pop_mgr();
}
return this.inst;
}
private get_ui(path:string):pop_ui
{
let ui:pop_ui = this.ui_cache[path];
if(!ui)
{
this.ui_cache[path] = ui = {node:null, is_show:false};
}
return ui;
}
clear()
{
for(let path in this.ui_cache)
{
this.hide(path);
}
this.ui_cache = {};
this.ui_stack.length = 0;
}
peek()
{
return this.ui_stack[this.ui_stack.length - 1];
}
set_handlers(on_ui_show:handler, on_ui_hide:handler)
{
this.ui_show_handler = on_ui_show;
this.ui_hide_handler = on_ui_hide;
}
is_show(path:string):boolean
{
let ui:pop_ui = this.ui_cache[path];
return ui != null;
}
show(path:string, transition?:UI_TRANSITION, ...params:any[]):void
{
let ui:pop_ui = this.get_ui(path);
if(ui.is_show)
{
return;
}
ui.is_show = true;
pool_mgr.get_inst().get_ui(path, gen_handler((node:cc.Node):void=>{
if(!ui.is_show)
{
pool_mgr.get_inst().put_ui(path, node);
return;
}
ui.node = node;
//应用过渡效果
this.applyTransitionEffect(node, transition);
cc.director.getScene().addChild(node);
TimerMgr.getInst().once(0, utils.gen_handler(() => {
//在加到场景同一帧调用界面show方法计算位置会不准确故统一在下一帧调用show
if(!ui.is_show)
{
return;
}
let ui_base:POP_UI_BASE = node.getComponent(POP_UI_BASE);
ui_base.ui_name = path;
ui_base.__show__(...params);
//进栈
this.ui_stack.push(path);
//钩子函数调用
if(this.ui_show_handler)
{
this.ui_show_handler.exec();
}
}));
}, this));
}
//关闭界面时不destroy只是从父节点移除并缓存
hide(path:string):void
{
let ui:pop_ui = this.ui_cache[path];
if(!ui)
{
return;
}
this.ui_cache[path] = null;
ui.is_show = false;
if(ui.node)
{
pool_mgr.get_inst().put_ui(path, ui.node);
//调用hide
let ui_base:POP_UI_BASE = ui.node.getComponent(POP_UI_BASE);
ui_base.__hide__();
//出栈
const lastIdx = this.ui_stack.lastIndexOf(path);
if(lastIdx != -1)
{
this.ui_stack.splice(lastIdx, 1);
}
//钩子函数调用
if(this.ui_hide_handler)
{
this.ui_hide_handler.exec();
}
}
}
applyTransitionEffect(node:cc.Node, transition:UI_TRANSITION)
{
if(transition && transition.transType == UI_TRANSITION_TYPE.None)
{
return;
}
transition = transition || {
transType:UI_TRANSITION_TYPE.FadeIn,
duration:0.5,
tweenFunc:TweenFunc.Linear
};
switch(transition.transType)
{
case UI_TRANSITION_TYPE.FadeIn:
TweenUtil.from({node, duration:transition.duration || 1, opacity:0, tweenFunc:transition.tweenFunc || TweenFunc.Linear});
break;
}
}
}
type pop_ui = {
node:cc.Node;
is_show:boolean;
}
//界面prefab路径配置, 相对于assets/resources目录
export const UI_CONFIG = {
overlay_bg:"panels/panel_overlay_bg",
level:"panels/panel_level",
level_detail:"panels/panel_leveldetail",
game:"panels/panel_game",
level_result:"panels/panel_levelresult",
level_reward:"panels/panel_levelreward",
rank:"panels/panel_rank",
newbee_gift:"panels/panel_newbeegift",
sos_gift:"panels/panel_sosgift",
login_gift:"panels/panel_logingift",
}
interface UI_TRANSITION
{
transType:UI_TRANSITION_TYPE;
tweenFunc?:Function;
duration?:number;
}
export const enum UI_TRANSITION_TYPE
{
None = 1,
FadeIn,
DropDown,
PopUp,
LeftIn,
RightIn,
import { pool_mgr } from "../pool/pool_mgr"
import { gen_handler } from "../util"
import { POP_UI_BASE } from "./pop_ui_base"
import { TweenUtil } from "../tween/tweenutil"
import { TweenFunc } from "../tween/tweenfunc"
import { event_mgr, Event_Name } from "../event/event_mgr";
import { LoadingQueue } from "../loader/loading_queue";
const panel_overlay_bg = "panel_overlay_bg";
const isDualInstanceView = (path:string) => {
const config:UI_CONFIG = UI_CONFIG_MAP[path];
return config && config.dualInstance;
}
const isModalView = (path:string) => {
const config:UI_CONFIG = UI_CONFIG_MAP[path];
return !config || config.needOverlayBg != false;
}
const isFullScreenView = (path:string) => {
const config:UI_CONFIG = UI_CONFIG_MAP[path];
return config && config.fullScreen;
}
const isDestroyAtOnce = (path:string) => {
const config:UI_CONFIG = UI_CONFIG_MAP[path];
return config && config.destroyAtOnce;
}
const getOverlayBgOpacity = (path:string) => {
const config:UI_CONFIG = UI_CONFIG_MAP[path];
if(!config || config.overlayBgOpacity == null) {
return 175;
}
return config.overlayBgOpacity;
}
export class pop_mgr
{
private static inst:pop_mgr;
private view_map:Map<string, PopView[]>;
private view_stack:PopView[];
private overlayNode:cc.Node;
private constructor()
{
this.view_map = new Map();
this.view_stack = [];
event_mgr.get_inst().add(Event_Name.UI_SHOW, this.onViewShow, this);
event_mgr.get_inst().add(Event_Name.UI_HIDE, this.onViewHide, this);
}
static get_inst():pop_mgr
{
if(!this.inst) {
this.inst = new pop_mgr();
}
return this.inst;
}
show(path:string, transition?:UI_TRANSITION, ...params)
{
let views = this.view_map.get(path);
if(!isDualInstanceView(path) && views && views.length > 0) {
return views[0];
}
if(!views) {
views = [];
this.view_map.set(path, views);
}
const view = new PopView(path);
const scene = cc.director.getScene();
if(cc.isValid(scene)) {
const viewRoot = scene.getChildByName("Canvas");
if(cc.isValid(viewRoot)) {
view.setParent(viewRoot);
view.setTransition(transition);
view.setModal(isModalView(path));
view.show(UI_CONFIG_MAP[path], ...params);
views.push(view);
}
}
return view;
}
/** deprecated */
hide_bypath(path:string)
{
const views = this.view_map.get(path);
if(!views || views.length <= 0 || isDualInstanceView(path)) {
return;
}
const view = views.pop();
view.hide();
}
has_views()
{
let ret = false;
this.view_map.forEach(views => {
if(views.length > 0) {
ret = true;
}
});
if(!ret) {
ret = this.view_stack.length > 0;
}
return ret;
}
top_view()
{
if(this.view_stack.length > 0) {
return this.view_stack[this.view_stack.length - 1];
}
return null;
}
clear()
{
this.view_map.forEach(views => {
views.forEach(view => view.hide());
});
this.view_map.clear();
this.view_stack.length = 0;
if(cc.isValid(this.overlayNode)) {
this.overlayNode.destroy();
this.overlayNode = null;
}
cc.log(`PopMgr clear`);
}
private onViewShow(view:PopView)
{
cc.log(`PopMgr, onViewShow, view path=${view.getPath()}`);
this._pushViewToStack(view);
this._updateOverlay();
}
private onViewHide(view:PopView)
{
cc.log(`PopMgr, onViewHide, view path=${view.getPath()}`);
this._removeViewFromMap(view);
this._popViewFromStack(view);
this._updateOverlay();
}
private _removeViewFromMap(view:PopView)
{
const path = view.getPath();
const views = this.view_map.get(path);
if(views) {
const idx = views.indexOf(view);
if(idx != -1) {
views.splice(idx, 1);
}
}
}
private _pushViewToStack(view:PopView)
{
if(this.view_stack.indexOf(view) == -1) {
this.view_stack.push(view);
}
}
private _popViewFromStack(view:PopView)
{
const idx = this.view_stack.indexOf(view);
if(idx != -1) {
this.view_stack.splice(idx, 1);
}
}
private _updateOverlay()
{
//找到最上层模态view
const topModalView = this.getToppestModalView();
if(!topModalView) {
this._removeOverlay();
return;
}
this._addOverlay(topModalView);
}
private _removeOverlay()
{
const overlayNode = this.overlayNode;
if(cc.isValid(overlayNode) && overlayNode.parent) {
overlayNode.removeFromParent(true);
overlayNode.destroy();
this.overlayNode = null;
cc.log(`PopMgr, removeOverlay`);
}
}
private _addOverlay(view:PopView)
{
cc.log(`PopMgr, _addOverlay, view=${view.getPath()}`);
const viewRoot = view.getParent();
if(!cc.isValid(viewRoot)) {
cc.log(`PopMgr, _addOverlay, invalid viewRoot 1`);
return;
}
const overlayNode = viewRoot.getChildByName(panel_overlay_bg);
if(overlayNode) {
this.overlayNode = overlayNode;
this._addOverlayUnderView(overlayNode, view);
// this._debugOverlay(viewRoot, overlayNode);
return;
}
LoadingQueue.getInst().loadPrefabObj(UI_NAME.overlay_bg, gen_handler((overlayNode:cc.Node) => {
if(!cc.isValid(viewRoot)) {
cc.log(`PopMgr, _addOverlay, invalid viewRoot 2`);
return;
}
if(view != this.getToppestModalView()) {
// cc.log(`PopMgr, _addOverlay, ${view.getPath()} isn't toppest modal view`);
return;
}
this.overlayNode = overlayNode;
overlayNode.parent = viewRoot;
overlayNode.name = panel_overlay_bg;
this._addOverlayUnderView(overlayNode, view);
// this._debugOverlay(viewRoot, overlayNode);
}));
}
private _addOverlayUnderView(overlayNode:cc.Node, view:PopView)
{
cc.log(`PopMgr, _addOverlayUnderView, view=${view.getPath()}`);
overlayNode.opacity = getOverlayBgOpacity(view.getPath());
const viewZOrder = view.getNode().getSiblingIndex();
if(overlayNode.getSiblingIndex() < viewZOrder) {
overlayNode.setSiblingIndex(viewZOrder - 1);
}
else {
overlayNode.setSiblingIndex(viewZOrder);
}
}
private getToppestModalView()
{
const len = this.view_stack.length;
if(len <= 0) {
return null;
}
let modalView:PopView;
for(let i = len - 1; i >= 0; i--) {
const view = this.view_stack[i];
const path = view.getPath();
if(isModalView(path) && !isFullScreenView(path)) {
modalView = view;
break;
}
}
return modalView;
}
private _debugOverlay(viewRoot:cc.Node, overlayNode:cc.Node)
{
cc.log("---------debug viewtree start-----------");
viewRoot.children.forEach(c => cc.log(c.name + "\n"));
cc.log(`overlay opcaity=${overlayNode.opacity}`);
cc.log("---------debug viewtree end-----------");
}
}
export class PopView
{
private _path:string;
private _node:cc.Node;
private _parent:cc.Node;
private _posX:number;
private _posY:number;
private _transition:UI_TRANSITION;
private _config:UI_CONFIG;
private _params:any[];
private _isActive:boolean;
private _isModal:boolean;
private _isHide:boolean;
constructor(path:string)
{
this._path = path;
pool_mgr.get_inst().get_ui(path, gen_handler((node:cc.Node)=>{
if(this._isHide) {
pool_mgr.get_inst().put_ui(path, node);
return;
}
this._node = node;
if(cc.isValid(this._parent)) {
this._setParent(this._parent, this._posX, this._posY);
}
if(this._transition != null) {
this._setTransition(this._transition);
}
if(this._params != null) {
this._show(this._config, ...this._params);
}
if(this._isActive != null) {
this._setActive(this._isActive);
}
if(this._isModal != null) {
this._setModal(this._isModal);
}
}, this));
}
show(config:UI_CONFIG, ...params)
{
this._config = config;
this._params = params;
if(this.isValid()) {
this._show(config, ...params);
}
}
private _show(config:UI_CONFIG, ...params)
{
const path = this._path;
cc.log(`PopView show, path=${path}`);
event_mgr.get_inst().fire(Event_Name.UI_SHOW, this);
const uiBase = this._node.getComponent(POP_UI_BASE);
uiBase.setView(this);
uiBase.__show__(...params);
}
hide()
{
const path = this._path;
if(this._isHide) {
cc.log(`PopView hide, path=${path}, view is already hided`);
return;
}
this._isHide = true;
if(cc.isValid(this._node)) {
const uiBase = this._node.getComponent(POP_UI_BASE);
uiBase.__hide__();
uiBase.setView(null);
pool_mgr.get_inst().put_ui(path, this._node, isDestroyAtOnce(path));
cc.log(`PopView hide, path=${path}`);
event_mgr.get_inst().fire(Event_Name.UI_HIDE, this);
}
}
setParent(parent:cc.Node, x = 0, y = 0)
{
this._parent = parent;
this._posX = x;
this._posY = y;
if(this.isValid()) {
this._setParent(parent, x, y);
}
}
private _setParent(parent:cc.Node, x = 0, y = 0)
{
this._node.setParent(parent);
this._node.setPosition(x, y);
}
setActive(active:boolean)
{
this._isActive = active;
if(this.isValid()) {
this._setActive(active);
}
}
private _setActive(active:boolean)
{
this._node.active = active;
}
setModal(isModal:boolean)
{
if(this._isModal == isModal) {
return;
}
this._isModal = isModal;
if(this.isValid()) {
this._setModal(isModal);
}
}
private _setModal(isModal:boolean)
{
const hasComp = this._node.getComponent(cc.BlockInputEvents);
if(isModal && !hasComp) {
this._node.addComponent(cc.BlockInputEvents);
}
else if(!isModal && hasComp) {
this._node.removeComponent(cc.BlockInputEvents);
}
}
setTransition(transition:UI_TRANSITION)
{
this._transition = transition;
if(this.isValid()) {
this._setTransition(transition);
}
}
private _setTransition(transition:UI_TRANSITION)
{
transition = transition || {transType:UI_TRANSITION_TYPE.None};
switch(transition.transType)
{
case UI_TRANSITION_TYPE.FadeIn:
TweenUtil.from({target:this._node, duration:transition.duration || 1, opacity:0, tweenFunc:transition.tweenFunc || TweenFunc.Linear});
break;
}
}
private isValid()
{
return cc.isValid(this._node) && !this._isHide;
}
getPath()
{
return this._path;
}
getParent()
{
return this._parent;
}
getNode()
{
return this._node;
}
}
//界面prefab路径配置, 相对于assets/resources目录
export const UI_NAME = {
overlay_bg:"prefabs/panels/panel_overlay_bg",
settlement:"prefabs/panels/panel_settlement",
setting:"prefabs/panels/panel_setting",
msgBox:"prefabs/panels/panel_msgBox",
}
const UI_CONFIG_MAP = {
[UI_NAME.msgBox]: {dualInstance:true},
[UI_NAME.settlement]: {fullScreen:true, destroyAtOnce:true},
}
export interface UI_CONFIG
{
needOverlayBg?:boolean;
overlayBgOpacity?:number;
dualInstance?:boolean;
fullScreen?:boolean;
destroyAtOnce?:boolean;
}
interface UI_TRANSITION
{
transType:UI_TRANSITION_TYPE;
tweenFunc?:Function;
duration?:number;
}
export const enum UI_TRANSITION_TYPE
{
None = 1,
FadeIn,
DropDown,
PopUp,
LeftIn,
RightIn,
}

View File

@@ -1,101 +1,80 @@
import {pop_mgr, UI_CONFIG} from "./pop_mgr"
import {pool_mgr} from "../pool/pool_mgr"
import {handler, gen_handler} from "../util"
import * as Audio from "../../common/audio/audioplayer"
import {wxHttpClient} from "../../common/wxapi/index"
const {ccclass, property} = cc._decorator;
const pop_overlay_bg:string = "panel_overlay_bg";
@ccclass
export class POP_UI_BASE extends cc.Component {
@property(cc.Button)
btn_close: cc.Button = null;
//界面名字UI_CONFIG.*
private _ui_name:string;
protected is_show:boolean;
/*由于pop_mgr缓存策略此方法只会在首次打开界面时调用1次, 缓存以后再打开不会执行onLoad.
因此不能用来做每次打开界面时的初始化工作
*/
// onLoad()
// {
// cc.info("on_load 00000000000000000");
// }
set ui_name(value:string)
{
this._ui_name = value;
}
/**
* 只能由pop_mgr调用
*/
__show__(...params:any[]):void
{
cc.info("show", this._ui_name, ...params);
if(this.btn_close)
{
this.btn_close.node.on(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
}
this.is_show = true;
this.on_show(...params);
//添加遮罩背景
let overlay:cc.Node = this.node.getChildByName(pop_overlay_bg);
if(!overlay)
{
pool_mgr.get_inst().get_ui(UI_CONFIG.overlay_bg, gen_handler((bg_node:cc.Node):void=>{
if(!this.is_show || this.node.getChildByName(pop_overlay_bg))
{
pool_mgr.get_inst().put_ui(UI_CONFIG.overlay_bg, bg_node);
return;
}
bg_node.name = pop_overlay_bg;
this.node.addChild(bg_node);
bg_node.setSiblingIndex(0);
}, this));
}
}
/**
* 只能由pop_mgr调用
*/
__hide__():void
{
cc.info("hide", this._ui_name);
if(this.btn_close)
{
this.btn_close.node.off(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
}
this.is_show = false;
this.on_hide();
wxHttpClient.unregisterCtxHandler(this);
}
/**弹出界面时调用且在onLoad之后调用可以用来做一些初始化工作*/
on_show(...params:any[]):void
{
}
/**关闭界面时调用,用来做清理工作*/
on_hide():void
{
}
/**关闭自身*/
hide():void
{
pop_mgr.get_inst().hide(this._ui_name);
}
onCloseBtnTouch():void
{
this.hide();
Audio.AudioPlayer.getInst().play_sound(Audio.AUDIO_CONFIG.Audio_Btn);
}
import { PopView } from "./pop_mgr"
import { BaseUIComponent } from "./base_ui_component";
const {ccclass, property} = cc._decorator;
@ccclass
export class POP_UI_BASE extends BaseUIComponent {
@property(cc.Button)
btn_close: cc.Button = null;
protected is_show:boolean;
protected view:PopView;
setView(value:PopView)
{
this.view = value;
}
/**
* 只能由pop_mgr调用
*/
__show__(...params)
{
if(this.btn_close)
{
this.btn_close.node.on(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
}
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchSelf, this);
this.is_show = true;
this.on_show(...params);
this.enabled = true;
}
/**
* 只能由pop_mgr调用
*/
__hide__()
{
if(this.btn_close)
{
this.btn_close.node.off(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
}
this.node.off(cc.Node.EventType.TOUCH_END, this.onTouchSelf, this);
this.is_show = false;
this.enabled = false;
this.clearEventListeners();
this.on_hide();
}
/**弹出界面时调用,可以用来做初始化工作*/
protected on_show(...params)
{
}
/**关闭界面时调用,用来做清理工作*/
protected on_hide()
{
}
/**关闭自身*/
hide()
{
if(this.view) {
this.view.hide();
}
}
protected onCloseBtnTouch()
{
this.hide();
}
protected onTouchSelf(event:cc.Event.EventTouch)
{
}
}

138
ui/scene_mgr.ts Normal file
View File

@@ -0,0 +1,138 @@
import { BaseScene } from "./base_scene";
import { event_mgr, Event_Name } from "../event/event_mgr";
import { AudioPlayer } from "../audio/audioplayer";
import { DragonBoneFactory } from "../effect/dragonbone_factory";
import { ParticleFactory } from "../effect/particle_factory";
import { pop_mgr } from "./pop_mgr";
import { loader_mgr } from "../loader/loader_mgr";
import { Toast } from "./toast";
import { pool_mgr } from "../pool/pool_mgr";
import { LoadingQueue } from "../loader/loading_queue";
export class SceneMgr
{
private static _inst:SceneMgr
private _loadingSceneName:string;
private _currSceneName:string;
private _currScene:BaseScene;
private constructor()
{
cc.director.on(cc.Director.EVENT_BEFORE_SCENE_LAUNCH, this.onSceneWillLaunch, this);
}
static getInst():SceneMgr
{
if(!this._inst) {
this._inst = new SceneMgr();
}
return this._inst;
}
get currScene()
{
return this._currSceneName;
}
preloadScene(sceneName:string)
{
cc.director.preloadScene(sceneName);
}
loadScene(sceneName:string, ...params)
{
//reload current scene
if(this._currSceneName == sceneName) {
if(this._currScene && cc.isValid(this._currScene.node)) {
this.closeAllView();
this._currScene.__beforeDestroy__();
this._currScene.__onStarted__(...params);
event_mgr.get_inst().fire(Event_Name.SCENE_CHANGED, sceneName);
}
return;
}
if(this._loadingSceneName) {
return;
}
this._loadingSceneName = sceneName;
cc.director.loadScene(sceneName, (_, scene:cc.Scene) => {
this._loadingSceneName = null;
if(!cc.isValid(scene)) {
cc.log(`SceneMgr, loadScene scene = null`);
return;
}
//destroy old scene
const oldDestroy = scene.destroy;
scene.destroy = () => {
this.onSceneWillDestroy();
return oldDestroy.call(scene);
};
//make sure each scene has a child named "Canvas"
const baseScene = scene.getChildByName("Canvas").getComponent(BaseScene);
if(baseScene && cc.isValid(baseScene.node)) {
this._currScene = baseScene;
this._currSceneName = sceneName;
baseScene.setName(sceneName);
baseScene.__onStarted__(...params);
event_mgr.get_inst().fire(Event_Name.SCENE_CHANGED, sceneName);
}
});
}
private closeAllView()
{
//旧场景destroy前关闭所有已打开的界面
pop_mgr.get_inst().clear();
pool_mgr.get_inst().clear();
}
private onSceneWillDestroy()
{
if(this._currScene && cc.isValid(this._currScene.node)) {
this.closeAllView();
this._currScene.__beforeDestroy__();
this._currScene = null;
this._currSceneName = null;
}
}
private onSceneWillLaunch(newScene:cc.Scene)
{
//收集新场景依赖的资源
const excludeMap = {};
const newSceneAssets = newScene.dependAssets;
if(newSceneAssets) {
newSceneAssets.forEach(a => {
excludeMap[a] = true;
// cc.log(`newSceneAssets, asset=${a}`);
});
}
//收集持久节点依赖的资源
const toast = newScene.getChildByName("toast");
if(toast) {
cc.log("toast in current scene");
const deps = cc.loader.getDependsRecursively(Toast.resPath);
deps.forEach(d => {
excludeMap[d] = true;
// cc.log("prefabs/misc/toast deps=" + d);
});
}
//激活新场景前释放旧场景资源
Toast.clear();
AudioPlayer.getInst().clear_cache();
DragonBoneFactory.getInst().releaseAll();
ParticleFactory.getInst().releaseAll();
LoadingQueue.getInst().clear();
loader_mgr.get_inst().releaseAll(excludeMap);
}
}
export const SCENE_NAME = {
launch:"hotupdate",
main:"newmain",
battle:"battle",
}

112
ui/toast.ts Normal file
View File

@@ -0,0 +1,112 @@
import { loader_mgr } from "../loader/loader_mgr";
import { gen_handler } from "../util";
export class Toast
{
public static resPath = "prefabs/misc/toast";
private static inst:Toast;
private nodePool:cc.Node[];
private constructor()
{
this.nodePool = [];
}
private static getInst()
{
if(!this.inst)
{
this.inst = new Toast();
}
return this.inst;
}
private run(node:cc.Node)
{
node.parent = cc.director.getScene();
cc.game.addPersistRootNode(node);
node.opacity = 0;
node.setPosition(375, 0);
const moveUp = cc.moveBy(0.2, cc.v2(0, 117));
const fadeIn = cc.fadeIn(0.2);
const moveFadeAct = cc.spawn(moveUp, fadeIn);
const delayAction = cc.delayTime(1);
const fadeOut = cc.fadeOut(0.5);
const onFinished = cc.callFunc(() => {
cc.game.removePersistRootNode(node);
node.removeFromParent();
this.nodePool.push(node);
}, this);
node.runAction(cc.sequence(moveFadeAct, delayAction, fadeOut, onFinished));
}
private configure(node:cc.Node, params:ToastParmas)
{
const label = node.getChildByName("txt").getComponent(cc.Label);
label.string = params.txt;
label.node.color = params.txtColor || cc.color(255, 255, 255);
//label size
label.overflow = cc.Label.Overflow.NONE;
label._updateRenderData(true);
let labelWidth = label.node.width;
let labelHeight = label.node.height;
if(labelWidth > 500) {
labelWidth = 500;
label.node.width = labelWidth;
label.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
label._updateRenderData(true);
labelHeight = label.node.height;
if(labelHeight > 90) {
labelHeight = 90
label.node.height = labelHeight;
label.overflow = cc.Label.Overflow.SHRINK;
}
}
//bg size
const bg = label.node.parent;
let bgWidth = Math.max(397, labelWidth + 50);
let bgHeight = Math.max(76, labelHeight + 40);
bg.setContentSize(bgWidth, bgHeight);
this.run(node);
}
private make(params:ToastParmas)
{
if(this.nodePool.length > 0)
{
this.configure(this.nodePool.pop(), params);
}
else
{
loader_mgr.get_inst().loadPrefabObj(Toast.resPath, gen_handler((node:cc.Node) => {
this.configure(node, params);
}));
}
}
private clear()
{
this.nodePool.forEach(node => node.destroy());
this.nodePool.length = 0;
}
static show(params:ToastParmas)
{
Toast.getInst().make(params);
}
static clear()
{
Toast.getInst().clear();
}
}
interface ToastParmas
{
txt:string;
txtColor?:cc.Color;
}

141
util.ts
View File

@@ -1,81 +1,62 @@
import {loader_mgr} from "../common/loader/loader_mgr"
import * as consts from "../consts"
let handler_pool:handler[] = [];
let handler_pool_size = 10;
//用于绑定回调函数this指针
export class handler
{
private cb:Function;
private host:any;
private args:any[];
constructor(){}
init(cb:Function, host = null, ...args)
{
this.cb = cb;
this.host = host;
this.args = args;
}
exec(...extras)
{
this.cb.apply(this.host, this.args.concat(extras));
}
}
export function gen_handler(cb:Function, host:any = null, ...args:any[]):handler
{
let single_handler:handler = handler_pool.length < 0 ? handler_pool.pop(): new handler()
//这里要展开args, 否则会将args当数组传给wrapper, 导致其args参数变成2维数组[[]]
single_handler.init(cb, host, ...args);
return single_handler;
}
export function load_img(sprite, img_path)
{
loader_mgr.get_inst().loadAsset(img_path, gen_handler((res) => {
sprite.spriteFrame = res;
}), cc.SpriteFrame);
}
export function load_external_img(sprite:cc.Sprite, img_url:string, type?:string)
{
// console.log(`load_external_img ${img_url}`);
loader_mgr.get_inst().loadExternalAsset(img_url, gen_handler((res) => {
// console.log(sprite.spriteFrame, res, (res instanceof cc.Texture2D));
sprite.spriteFrame = new cc.SpriteFrame(res);
}), type);
}
export function strfmt(fmt:string, ...args:any[])
{
return fmt.replace(/\{(\d+)\}/g, (match:string, argIndex:number) => {
return args[argIndex] || match;
});
}
export function extend(target, ...sources) {
for (var i = 0; i < sources.length; i += 1) {
var source = sources[i];
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
}
export function createBreathAction(node:cc.Node)
{
const action = cc.repeatForever(cc.sequence(cc.scaleTo(0.6, 1.1), cc.scaleTo(0.6, 0.9)));
node.runAction(action);
}
export function destroyBreathAction(node:cc.Node)
{
node.stopAllActions();
let handler_pool:handler[] = [];
let handler_pool_size = 10;
//用于绑定回调函数this指针
export class handler
{
private cb:Function;
private host:any;
private args:any[];
constructor(){}
init(cb:Function, host = null, ...args)
{
this.cb = cb;
this.host = host;
this.args = args;
}
exec(...extras)
{
this.cb.apply(this.host, this.args.concat(extras));
}
}
export function gen_handler(cb:Function, host:any = null, ...args:any[]):handler
{
let single_handler:handler = handler_pool.length < 0 ? handler_pool.pop(): new handler()
//这里要展开args, 否则会将args当数组传给wrapper, 导致其args参数变成2维数组[[]]
single_handler.init(cb, host, ...args);
return single_handler;
}
export function strfmt(fmt:string, ...args:any[])
{
return fmt.replace(/\{(\d+)\}/g, (match:string, argIndex:number) => {
return args[argIndex] || match;
});
}
export function extend(target, ...sources) {
for (var i = 0; i < sources.length; i += 1) {
var source = sources[i];
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
}
export function createBreathAction(node:cc.Node, min = 0.9, max = 1.1)
{
const action = cc.repeatForever(cc.sequence(cc.scaleTo(0.6, max), cc.scaleTo(0.6, min)));
node.runAction(action);
}
export function destroyBreathAction(node:cc.Node)
{
node.stopAllActions();
}