初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
test:
docker:
- image: circleci/node:8.9.4
steps:
- checkout
- run: sudo npm install -g gulp
- run: npm install gulp-qunit
- run: npm install
- run: npm test
workflows:
version: 2
test:
jobs:
- test

12
engine/.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

24
engine/.eslintrc.json Normal file
View File

@@ -0,0 +1,24 @@
{
"extends": "eslint:recommended",
"rules": {
"comma-dangle": 0,
"no-console": 0,
"no-constant-condition": 0,
"semi": 1
},
"env": {
"browser": true,
"node": true,
"es6": true,
"mocha": true
},
"plugins": [
],
"globals": {
"cc": false,
"CC_EDITOR": false,
"CC_DEV": false,
"CC_JSB": false,
"_ccsg": false
}
}

32
engine/.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,32 @@
<!--
EN: Here only receive bug report or suggestions for improvement. Feature request please send to the forum: http://discuss.cocos2d-x.org/c/creator
中: 这里只接收 bug 反馈或改进建议。新功能需求请发到论坛https://forum.cocos.com/c/Creator
-->
### Creator version?(版本号)
### Affected platform?(受影响的平台)
<!-- Mac Editor / Windows Editor / Web / iOS / Android / Simulator ... -->
### How to reproduce?(如何重现)
### JavaScript output or error produced?(报错信息和调用栈)
<!--
EN: The error must be the first error when the problem occurred.
中:这里填写的报错信息必须是出现问题时的第一个报错。
-->
### Demo project?demo 项目)
<!--
EN: A complete parsable Creator project or resources exhibiting the issue with Creator alone - without third party tools or libraries or server. Ideally the demo should be as small as possible.
It is very likely that issues without a reproducible test case will be closed.
中:用于呈现错误的可理解的 Creator 工程或资源,不依赖第三方工具或插件或者服务端。这个 demo 应该越小越好。
不含可重现范例的 issue 将有可能被关闭。
-->
<!--
### Thanks for the feedback.(感谢反馈)
-->

16
engine/.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,16 @@
Re: cocos-creator/2d-tasks#
Changes:
*
<!-- Note: Makes sure these boxes are checked before submitting your PR - thank you!
- [ ] If your pull request has gone "stale", you should **rebase** your work on top of the latest version of the upstream branch.
- [ ] If your commit history is full of small, unimportant commits (such as "fix pep8" or "update tests"), **squash** your commits down to a few, or one, discreet changesets before submitting a pull request.
- For official teams:
- [ ] Check that your javascript is following our [style guide](https://docs.cocos.com/creator/manual/zh/scripting/reference/coding-standards.html) and end files with a newline
- [ ] Document new code with comments in source code based on [API Docs](https://github.com/cocos-creator/fireball#api-docs)
- [ ] Make sure any **runtime** log information in `cc.log`, `cc.error` or `new Error()` has been moved into `EngineErrorMap.md` with an ID, and use `cc.logID(id)` or `new Error(cc.debug.getError(id))` instead.
-->

16
engine/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
/lib
/bin
/web.config
.idea
.DS_Store
build
aspnet_client
node_modules
/tools/jsdoc_toolkit-2.4.0
/package
/tools/jsdoc_toolkit/jsdoc_toolkit-2.4.0
/.project
npm-debug.log
DebugInfos.json
cocos2d/core/platform/deserialize-compiled.js
cocos2d/core/value-types/*.js

3
engine/.gitpod.Dockerfile vendored Normal file
View File

@@ -0,0 +1,3 @@
FROM gitpod/workspace-full
RUN bash -c ". ~/.nvm/nvm-lazy.sh && npm install -g gulp"

21
engine/.gitpod.yml Normal file
View File

@@ -0,0 +1,21 @@
image:
file: .gitpod.Dockerfile
tasks:
- command: gp await-port 8511 && sleep 3 && gp preview $(gp url 8511)/bin/qunit-runner.html
- name: Build
init: |
npm install
npm install gulp-qunit express
gp sync-done init
command: gulp build
- name: Test
init: |
gp sync-await init
npm test
command: node test/qunit/server.js
openMode: split-right
ports:
- port: 8511
onOpen: ignore

45
engine/.jshintrc Normal file
View File

@@ -0,0 +1,45 @@
{
"node": true,
"esnext": true,
"bitwise": false,
"camelcase": false,
"curly": false,
"eqeqeq": true,
"immed": true,
"indent": 4,
"latedef": false,
"newcap": false,
"noarg": true,
"quotmark": "single",
"undef": true,
"unused": true,
"strict": false,
"predef": [
"window",
"document",
"Image",
"unused",
"Editor",
"EditorUI",
"Helper",
"_Scene",
"Polymer",
"sinon",
"after",
"afterAll",
"afterEach",
"assert",
"before",
"beforeAll",
"beforeEach",
"describe",
"expect",
"it",
"cc",
"CC_JSB",
"CC_EDITOR",
"CC_DEV",
"CC_TEST",
"_ccsg"
]
}

13
engine/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"files.exclude": {
".git": true,
"node_modules": true,
".github": true
},
"search.exclude": {
".git": true,
"node_modules": true,
".github": true,
"bin": true
}
}

297
engine/AUTHORS.txt Normal file
View File

@@ -0,0 +1,297 @@
Cocos2d-html5 authors
(Ordered by join time)
Core Developers:
Shun Lin (Sean Lin)
Ricardo Quesada
Huabin LING (@pandamicro)
Sijie Wang (@VisualSJ)
Huabing Xu (@dabingnn)
Guanghui Qu (@zilongshanren)
Jiawei Guo (@jareguo)
Kaile Yin (@2youyouo2)
Yanbin Huang (@knoxHuang)
Contributors:
Name GithubID Main contribution
Dali Kilani @dadilcool added instruction to read me
Chris @hannon235 added node.js api for box2d
added SocketIO and SocketIO tests
Jason Aeschliman @jaeschliman fixed cc.Node setposition
Zhuoshi Sun(Intel) @sunzhuoshi engine loader fixed
Alejandro Reyero @KaTXi fixed error message
Long Xiang @seanlong resource loader count
Timm Drevensek(Zynga) @timmjd cc.Class fixed
add CCScale9Sprite test
Lzzy Chen @czizzy audio engine fixed
cleanup comments
Mcscooter @mcscooter tilempapxml fixed
Roed @roed Mouse rightclick api
Mouse scroll fix for firefox
cc.Screen bug fix
Surith Thekkiam(Zynga) @folecr match Cocos2dx api
Robert Boyd @rboyd fixed stackable actions
Ivo Wetzel(Zynga Germany) @BonsaiDen Fixes for Audio Support Detection,
Texture Support in Particle System,
and Performance improvements
Mario Adrian @many20 Code review, multi touch improvements,
ccbi bugs fixed
keisuke hata(Square) @Seasons7 Code review, bug fix
Marat Yakupov @moadib Various bug fixes
Liang Wu @akira-cn Touch location fix for designResolution
ScrollView on paused bugs fix
Jimmy Sambuo @jsambuo AudioEngine improvements
Jose Antonio Andujar Clavell Scale9Sprite improvements
TadeuszWlodarkiewic @TadeuszWlodarkiewic Tizen Compatibility
smshuja @smshuja Parallax node bug fix
rickms @rickms EGLView Optimizations
Szymon Piłkowski @ardcore CCTexture Bug fix
Tomasz Tunik @tomasztunik CCNode Memory leak fix
Xuankang Lin(Intel) @AndriyLin cc.WebAudioEngine implements
Kang-Hao Lu(Opera/Oupeng) @kennyluck Optimize John Resig's inheritance pattern
cc.clone improvements
Mark Henderson @MarkEHenderson Code review, LabelTTF and Scale9Sprite bug fix
Jing Wang @06wj CCScheduler improvements
Js file loading image add
cc.RectApplyAffineTransform improvements
Fixed a bug of cc.Node.setPosition that parameter check is incorrect
Ze Wang @WanderWang Fix crash when BrowserTypes match nothing from navigator.userAgent
LabelTTF improvements
cc.TextureCache.dumpCachedTextureInfo 's bug fix
CCTween bug fix
CCScale9Sprite bug fix
sys.localStorage bug fix
CCArmature refactoring
cc.SpriteFrame and cc.SpriteFrameCache refactoring
Christian Schwartz @cschwartz SpriteFrame.initWithTextureFilename converted fix
XiaoJun Zheng @SmallAiTT _getResType error fix
cc.ScrollView bug fix
cc.LabelTTF bug fix
rewrite functions in CCNS.js with regex
refactor CCScheduler.js and CCCommon.js
cc.BMFontConfiguration bug fix
refactor cc.Application to cc.game
refactor cc.loader
refactor cc.Textur2D
refactor some functions about array operation
move sys.xxx to cc.sys.xxx
refactor some public functions in cc to private
add node.js scripts for publishing game
refactor cc.CCBReader
cc.view bug fix
multiple property object supports in extend function
Guozhu Cheng @bengol cc.SimpleAudioEngine bug fix
Jing Xiao @xbruce cc.SAXParser bug fix
cc.ActionManager refactoring
cc.Scheduler refactoring
cc.LabelTTF refactoring
JiaHui He @garfield_ho cc.FileUtils bug fix
cc.Builder and SpriteLoader bugs fix
CCBAnimation bug fix
Luis Parravicini @luisparravicini cc.Director refactoring
cc.LabelTTF documentation
typo fix
Jose Antonio @jandujar cc.ParticleSystem bug fix for CocoStudio animations
SceneReader bug fix
BoHao Tang @btspoony cc.TableView bug fix
Claudiu @csaftoiu cc.LabelTTF bug fix
cc.DrawNode bugs fix
cc.NodeRGBA bug fix
cc.Texture2D bug fix
Pei Wu @rablwupei cc.ProgressTimer bug fix
cc.ParticleBatchNode bug fix
kuaipao @kuaipao CocoStudio GUIReader bug fix
CocoStudio UIScrollView bug fix
CocoStudio UIWidget bug fix
XieDaijin @NijiadeIX CCControl bug fix
samael @samael65535 CCPhysicsSprite bug fix
NatWeiss @NatWeiss Add analytics plugin protocol ,Flurry plugin and ProtocolAds.js plugin protocol
cc.FileUtils refactoring
cc.Audio bugs fix
cc.Texture2D bug fix
Andor Salga @asalga typo fix
erykwalder @erykwalder Function.prototype.bind bug fix
ZippoLag @ZippoLag cc.Application.getCurrentLanguage bug fix
typo fix
Fixed `cc.TMXObjectGroup#objectNamed` not returning the result bug
Asano @LaercioAsano cc.Node bug fix
Bruno Assarisse @bassarisse cc.LabelBMFont bug fix
Mykyta Usikov @musikov cc.ClippingNode bugs fix
cc.fontLoader bug fix
Inverted ClippingNode with DrawNode as stencil bug fix under canvas render mode
JumpTo bug with wrong _delta position bug fix
cc.ProgressTimer bugs fix
cc.Scale9Sprite bugs fix
cc.RenderTexture bug fix
cc.ParticleSystem bug fix
Made CCProgressTimerCanvasRenderCmd to properly show colorized sprites
cc.ScrollView and cc.TableView: added check for parent visibility in onTouchBegan method
Han XiaoLong @kpkhxlgy0 cc.ParticleSytem bug fix
AaronRZH @AaronRZH Creation of a sequence objcet or a spawn object by using new method bug fix
Xiaodong Liu @tianxing113 cc.Spawn.create bug fix
ccui.LoadingBar.setPercent crash bug fix
Park Hyun Chen @sincntx Touch anywhere of screen to finish input when using cc.EditBox
ccui.TextBMFont bug fix
cc.game bug fix
Fixed an issue of cc.ArmatureAnimation's setMovementEventCallFunc
Ninja Lau @mutoo A typo bug in UILayout fix
One-loop CCArmatureAnimation can't finish when setSpeedScale is less than 1.0 bug fix
A transform error in CCTransformHelp.js fix
ccs.DisplayManager bug fix
Fix child armature lost _parentBone issue
cc.eventManager bug fix
ccs.Bone bug fix
ccs.ActionFrame bug fix
ccui.Widget bug fix
ccui.LoadingBar bug fix
Taras Tovchenko @tovchenko cc.Skin bounding box calculation bug fix under canvas render mode
Minh Quy @MQuy cc.MenuItemSprite bug fix
Check empty string for textureData
Adds type check functions
Michael Yin @layerssss cc.game refactored
Yang Yuchen @yycdef cc.sys bug fix
K @kiwigrc cc.ParticleSystem bug fix
Claudio Freitas @claudiofreitas ccui.TextField typo fix.
nopakos @nopakos cc.Texture2D bug fix
Robert Rouhani @Robmaister cc.TMXMapInfo bug fix
cc.TMXLayer bug fix
Igor Mats @IgorMats cc.Scale9Sprite bug fix
Spine runtime update
Add getStroke and setStroke method to cc.MotionStreak
Tim @duhaibo0404 ccs.csLoader bug fix
Hermanto @man2 cc.loader bug fix
Long Jiang @jianglong0156 cc.LabelBMFont bug fix
KeyCode bug fix
ccui.ListView bug fix
Joe Lafiosca @lafiosca Added Javascript file loader
galapagosit @galapagosit ccs.actionManager bug fix
Dany Ellement @DEllement cc.FontDefinition & ccui.RichText improvements
cc.LayerGradient improvements
IShm @IShm cc.Screen bug fix
cc.ParticleSystem bug fix
ccui.PageView bug fix
Fixed crash when character not found into BMP font
Fixed restoring of sprite's color issue
Thomas Jablonski @thomas-jablonski cc.audioEngine bug fix
Cocostudio typo fix
WingGao @WingGao cc.TMXLayer bug fix
Skliar Ihor @igogo5yo Add Bower support
feijing566 @feijing566 cc.Audio bug fix
RackovychV @RackovychV Fixed a bug of `cc.Scheduler`'s `pauseAllTargetsWithMinPriority`
giuseppelt @giuseppelt Fixed TransitionSlideX callback sequence issue
YShumov @pixmaster Fixed issue in music end event
SPACELAN @spacelan Fixed `inverse` function bug of `cc.math.Matrix4`
patriciog @patriciog Allowed timeline animations with only one frame
Ningxin Hu @huningxin SIMD.js optimization for kazmath functions
Zachary Lester @ZLester Fix typo in AUTHORS.txt
Juan Carlos @Ruluk Fixed a bug where not resetting cc.Audio._ignoreEnded when replaying a sound caused it to stay in a "playing" state
Maxim Litvinov @metalim Throw new Error object instead of error message string
Retired Core Developers:
Shengxiang Chen (Nero Chan)
Xingsen Ma
Xiaojun Zheng
Jialong Zhai (@JoshuaAstray)
Hao Wu (WuHao)
Dingping Lv (David Lv)
Long Jiang (@jianglong0156)
Menghe Zhang (@ZhangMenghe)
Cocos2d-x and cocos2d-html5 can not grow so fast without the active community.
Thanks to all developers who report & trace bugs, discuss the engine usage in forum & QQ groups!
Special thanks to Ricardo Quesada for giving us lots of guidances & suggestions.

952
engine/CHANGELOG.txt Normal file
View File

@@ -0,0 +1,952 @@
ChangeLog:
Cocos2d-x v3.8 @ September.6 2015
[NEW] spine: Supported Spine runtime 2.3 (Both native and web engine)
[NEW] Animate: Added Animate's getCurrentFrameIndex function
[NEW] network: Upgrade SocketIO support to v1.x
[REFINE] web: Avoid re-bake the content when the parent node's position get changed
[REFINE] web: Added GameNodeObjectData and GameLayerObjectData in JSON parser
[REFINE] web: Updated skeleton animation to the latest version
[REFINE] web: Optimized resources automatic loading in JSON parser
[REFINE] web: Avoid cc.loader resource loading being terminated while encounter errors
[REFINE] web: Throw new Error object instead of error message string
[REFINE] web: Move setDepthTest to renderer
[REFINE] web: Added BlendFuncFrame parser
[REFINE] web: Permitted webp image loading on Chrome
[REFINE] web: Suspended the video player when the browser is minimized
[FIX] web: Fixed a bug that VideoPlayer remove event throw error
[FIX] web: Fixed Armature position error in studio JSON parser
[FIX] web: Fixed default clearColor error in director
[FIX] web: Fixed rotation value parsing error in the timeline parser
[FIX] web: Fixed a bug that nested animation may be affected by outer animation
[FIX] web: Made LabelAtlas ignoring invalid characters and updating correctly the content size
[FIX] web: Fixed a bug that VideoPlayer remove event throw error
[FIX] web: Fixed a bug that cc.director.setNotificationNode(null) doesn't take effect
[FIX] web: Fixed texture rect update issue while changing sprite frame
[FIX] web: Fixed effect issue in ActionGrid and NodeGrid
[FIX] web: Fixed logic issue in Menu's _onTouchCancelled function
[FIX] web: Fixed MenuItem crash when normal image is null
[FIX] web: Fixed incomplete fadeout effects
[FIX] web: Fixed issue that return value of cc.screen.fullScreen is not boolean
[FIX] web: Fixed a bug that SkeletonNode is not drawing children
[TEST] web: Rewrote testcase for stencil depth mask in RenderTextureTest
[TEST] web: Improved renderTexture stencilDepth test
[TEST] web: Fixed abnormal effects in effectsTest
[TEST] web: Fixed invisiable testcase of effects
Cocos2d-x v3.7.1 @ August.12 2015
[HIGHLIGHT] studio: Added new skeleton animation support and JSON parser for cocos v2.3.2 beta
[NEW] Node: Added getNodeToParentTransform with selected ancestor
[NEW] web: Added cc.director.setClearColor and support transparent background
[NEW] web: Added Animate's getCurrentFrameIndex function
[REFINE] studio: Optimized JSON parser's performance by removing audio play
[REFINE] studio: Optimized editor related extension data to a component instead of hosting in _userObject
[REFINE] web: Improved color/opacity manipulations in MenuItems
[FIX] studio: Fixed ccs.Skin construction issue in JSON parser
[FIX] web: Fixed an issue that loading process won't trigger callback problem
[FIX] web: Fixed a bug where not resetting cc.Audio._ignoreEnded when replaying a sound caused it to stay in a "playing" state
[FIX] web: cc.ScrollView and cc.TableView: added check for parent visibility in onTouchBegan method
[FIX] web: Fixed TurnPageDown effect
[FIX] web: Fixed Cocos Studio parser issue that all elements are missing while the timeline action contains rotation
Cocos2d-x v3.7 Final @ July 20 2015
[REFINE] web: Add compatible Uint16Array defintion
[FIX] web: Fixed url check regular expression not supporting localhost issue
[FIX] web: Fixed issue that sprite doesn't update texture rect correctly
Cocos2d-x v3.7 RC1 @ July 14 2015
[REFINE] Improved localStorage warning when disabled
[FIX] Fixed a bug that SingleNode's color isn't set
[FIX] studio: Fixed a bug of JSON parser that texture address is wrong
[FIX] Fixed MenuItems' color/opacity setter issue with child nodes
[FIX] Fixed page view's layout issue for JSON parser
[FIX] Add ttc loader and prevent the pure digital fonts is invalid
[FIX] Fixed Float32Array initialization
[FIX] Fixed a bug that layout background is missing
[FIX] Fixed a bug that ObjectExtensionData miss setCustomProperty and getCustomProperty function
Cocos2d-x v3.7 RC0 @ July 1 2015
* The json loader of Cocos Studio will automatically load dependencies resources
* SIMD.js optimization for kazmath functions (from Intel)
* Deleted the redundant variables defined and log informations in ccui.RichText
* Allowed timeline animations with only one frame
* Improved property declaration of cc.Texture2D
* Bug fixes:
1. Fixed positionType error of particle system in timeline parser
2. Fixed setAnimationName issue while the property is undefined in timeline parser
3. Fixed `cc.TMXObjectGroup#objectNamed` not returning the result bug
4. Fixed TransitionSlideX callback sequence issue
5. Fixed issue in music end event
6. Fixed bug that LayerColor's color will disappear when update transform after being baked
7. Fixed `inverse` function bug of `cc.math.Matrix4`
8. Fixed the webaudio's invalid loop attribute bug for chrome 42
9. Fixed crash when character not found into BMP font
10. Fixed spine's js parser issue by avoid NaN duration
11. Fixed LabelTTF multiline detection
12. Fixed issue in ccui.Widget#getScale
13. Fixed texture is not updated in some cases
14. PlayMusic should not use the search path (timeline 2.x)
15. Fixed bug of loading path of resources
16. Premultiply texture's alpha for png by default to fix Cocos Studio render issues
17. Fixed cache update issue of Layout after bake
18. Fixed isBaked returning undefined issue
19. Made CCProgressTimerCanvasRenderCmd to properly show colorized sprites
20. Fixed attributes being reset issue while baked cache canvas' size changed
21. Fixed texture does not rotate bug of ccui.LoadingBar
22. Fixed color not being set issue in timeline parser
23. Fixed custom easing animation bug
24. Fixed return empty texture2d bug when adding image with same url multiple times
25. Fixed actiontimeline can not step to last frame issue when loop play
26. Fixed the prompt can not be used in iOS wechat 6.2
27. Fixed restoring of sprite's color issue
28. Fixed Uint8Array initialize issue
29. Fixed cc.TextFieldTTF Delegate memory leaks
30. Fixed sorted result is wrong in cc.eventManager (_sortEventListenersOfSceneGraphPriorityDes)
31. Fixed BinaryLoader issue on IE11
32. Fixed the sprite's texture bug when frequently change the color
33. Fixed an issue that action will result in automatic termination
34. Fixed ScrollView initWithViewSize issue
Cocos2d-JS v3.6 @ April 29 2015
* Added GAF web runtime to the web engine, the native support will be merged in future version.
* Synchronised Cocos2d-x v3.6.
* Bug fixes:
1. Fixed a bug of Cocos Studio parser that it doesn't parse correctly the outline of text widget and button widget.
2. Fixed a bug of Cocos Studio parser that it doesn't support inner action correctly.
3. Fixed a bug of Cocos Studio parser that `ccui.Text`'s content size is set incorrectly.
4. Fixed a bug of Cocos Studio parser that `ccui.Layout`'s background color is set incorrectly.
5. Fixed a bug of `cc.Node`'s `removeAllChildren` that it doesn't notify the renderer to update.
6. Fixed a bug of audio system that the resume of music may start from the beginning.
7. Fixed a bug that sprite's `setTexture` fails to update its content size.
8. Fixed a bug that Scale9Sprite's children doesn't get transformed recursively.
9. Fixed constant naming issue of `ccs.FrameEaseType`.
10. Fixed `cc.LoaderScene.preload` API inconsistency between web engine and native engine.
11. Fixed a bug that `ccui.Slider` doesn't act correctly when it's scaled.
12. Fixed a bug that `ccui.Button` renders incorrectly when scale9sprite option enabled.
13. Fixed circular invocation issue in `cc.Sprite`'s canvas render command.
Cocos2d-JS v3.6 Beta @ April 22 2015
* Improved TMX transform to support RotationX and RotationY.
* Refactored Spine skeleton render command.
* Added checks to prevent issues when `cc.Node.WebGLRenderCmd` is not exist.
* Improved iOS browsers detection.
* Added getter setter function for `cc.MotionStreak`'s stroke property.
* Improved the detection of render mode.
* Upgraded Action Timeline and parser for the latest version of Cocos editor.
* Added `enumerateChildren` function for `cc.Node`.
* Make `cc.Scale9Sprite` support unpreloaded texture.
* Added `cc.sys.isObjectValid` to detect whether an object is still valid (in web and native engine).
* Bug fixes:
1. Fixed a bug that `cc.Scheduler`'s `scheduleOnce` runs multiply times.
2. Fixed a bug of `cc.Scheduler`'s `pauseAllTargetsWithMinPriority`.
3. Fixed a bug of `cc.eventManager` that its event listeners' order is incorrect when some nodes haven't been added to the scene graph or have been removed from parent without cleanup.
4. Fixed a bug of `cc.LabelTTF` that `enableShadow` doesn't work.
5. Fixed a bug of `cc.LabelTTF` that `setColor` doesn't set shadow color under Canvas render mode.
6. Fixed a bug that stopped audios can be resume after invoking pause on them.
7. Fixed a bug that `ccui.LoadingBar`'s texture renders incorrectly without preload.
8. Fixed a bug that cocos builder's callback doesn't get invoked.
9. Fixed a bug that TMX objects' position is incorrect when content scale factor is modified.
10. Fixed a mistaken usage of `cc.isObject` in `cc.Sprite` implementation.
11. Fixed a bug that position type haven't been copied in `cc.ParticleSystem`'s `clone` function.
12. Fixed some undefined parameter check issues in `cc.Node`.
13. Fixed a bug that setter for `scaleY` of `cc.EditBox` is incorrect.
14. Fixed a bug of `cc.SkeletonAnimation` that its canvas render command doesn't work correctly.
15. Fixed a parsing issue for the width of `cc.LabelBMFont`.
16. Fixed `ccs.TweenType`'s constants naming issue.
17. Fixed a bug that the spine skeleton may be rendered under the unsupported mode.
18. Fixed a bug when setting `cc.ParticleSystem`'s blend function in the ActionTimeline parser.
19. Added check to prevent issues that functions may not exist in the ActionTimeline parser.
20. Fixed a typo of `ccs.displayFactory`.
21. Fixed a bug of `cc.Node.setPosition` that parameter check is incorrect.
Cocos2d-JS v3.5 @ April 1 2015
* Upgraded Cocos Studio parser to support Cocos Studio v2.2.
* Upgraded Spine support to v2.1, added spine test case with FFD. FFD is supported in native but not in web, both engine can parse the new version file correctly, but the web engine will ignore FFD informations.
* Replaced '==' with '===' for better performance.
* Added `path` parameter in `ccs.load` to support modifying cocostudio project resource path.
* Added animationList to Cocostudio ActionTimeline to support playing animation by name.
* Made ParticleSystem support creation from an map object.
* Added missing functions to `cc.Grid3D` and `cc.PageTurn3D`.
* Added tip message functions to `cc.TextFieldTTF` for mobile browser.
* Added a function `cc.sys.openURL`.
* Disabled retina display by default for better performance.
* Added Bower support.
* Updated `cc.sys.OS_XXX` informations for supported systems.
* Bug fixes:
1. Fixed a bug of chipmunk.js that it doesn't work under closure compiler advanced mode.
2. Fixed a bug of Cocos Studio parser that widget didn't set its layout component.
3. Fixed grammatical mistakes in cocostudio parser logs.
4. Fixed memory leak issue in `cc.LabelBMFont`.
5. Fixed a bug of `cc.Scale9Sprite` that its `updateDisplayColor` doesn't take effect.
6. Fixed a bug of Cocos Studio parser that `cc.Scale9Sprite` doesn't display correctly if its texture isn't preloaded.
7. Fixed a bug of `cc.MenuItemSprite` that the construction will fail when parameter `selectedSprite` is a Scale9Sprite instance.
8. Fixed a bug of Cocos Studio parser that the background color of `ccui.Layout` can't be parsed correctly.
9. Fixed a bug of `cc.ClippingNode` that it doesn't work when set `inverted` to true in Canvas Mode.
10. Fixed a bug of `ccs.Armature` that its name was modified to animation name when loading from json files.
11. Fixed a bug of `ccui.PageView` that it cancel child touch during movment of page view.
12. Fixed a bug of `cc.Scheduler` that its parameter `repeat` is invalid in schedule function.
13. Fixed a bug of `cc.Scheduler` that `unschedule` function may fail.
Cocos2d-JS v3.4 Beta0 @ March 19 2015
* Added Windows Phone 8.0 platform support.
* Upgraded SpiderMonkey to v33, greatly improved JS object garbage collection and performance.
* Bound 3D modules including camera, light, sprite 3d, animation 3d, billboard, etc.
* Improved `cc.FontDefinition` & `ccui.RichText` in the web engine.
* Added gradient stops feature to `cc.LayerGradient` [Web exclusive].
* Upgraded `cc.Scheduler` in the web engine with Cocos2d-x v3.4 implementation.
* Added a loading screen when scripts are loading.
* Improved performance by replacing `Object.defineProperties` with `cc.defineGetterSetter`.
* Supported loading sprite frames from json object.
* Refactored math library to improve web engine performance.
* Removed some variables from `cc` namespace to improve web engine performance.
* Added the Firefox OS Web manifest to support Firefox OS apps.
* Added `cocos` attr to the script element in templates.
* Moved loading.js to res folder for Cocos Console release mode.
* Bug fixes:
1. Added `getSpriteFrame` to `cc.Sprite` to fix API inconsistency.
2. Added `getObject` to `cc.TMXObjectGroup` to fix API inconsistency.
3. Added `addImageAsync` to `cc.textureCache` to fix API inconsistency.
4. Fixed a bug of `cc.text` that its default font name is incorrect.
5. Fixed a bug of `ccui.PageView` that its `getPage` doesn't work.
6. Fixed a bug of `ccui.ImageView` that its `loadTexture` doesn't work while it's invoked multiple times at the same frame.
7. Fixed a bug of `ccui` that its load event callbacks have some mistakes.
8. Fixed a bug of `cc.Layer` that its bake function doesn't work when the layer has a parent node.
9. Fixed typos in `cc.ClippingNode.WebGLRenderCmd` and `cc.ParticleSystem.WebGLRenderCmd` creation.
10. Fixed a bug of `cc.Sprite` in `setTextureRect`.
11. Fixed a bug of `cc.Screen`.
12. Fixed a bug of `cc.view` that it doesn't work on iOS 8.1.2.
13. Fixed a bug of cc.DrawNode that its lineWidth is always to default value when set linewidth to zero.
14. Fixed a bug in hack for particles performance on canvas.
15. Fixed a bug of `cc.audioEngine` that it doesn't work after minified/compiled.
16. Fixed a bug in `CCBoot.js` that WebGL is not activated in web view of iOS 8.
17. Fixed a bug of `cc.CheckBox` that its position is incorrect when its texture isn't preloaded.
18. Fixed a bug of `cc.TMXLayer` that it stops to work after `setTileGID` called.
19. Fixed a bug of Cocos parser 2.x that it doesn't set widget's LayoutComponent.
20. Fixed a bug of `cc.isObject` that it considered function as an object.
Cocos2d-JS v3.3 @ Feb.9, 2015
* Upgraded spine runtime to support the latest version and updated its test case.
* Added an option "noCache" for debugging on browsers.
* Set the default value of `cc.ParticleSystem`'s draw mode to texture mode.
* Added message to `ccs.load` when loading armature json file.
* Improved particle system test case.
* Bug fixes:
1. Fixed a bug of `cc.Sprite` that its `setSpriteFrame` doesn't work when sprite frame's `rotated` property is true.
2. Fixed a bug of `cc.ClippingNode` when its stencil is `cc.Node` object in canvas mode.
3. Fixed a ccui bug that the position of widgets is incorrect after loaded v2.x json file with `ccs.load`.
4. Fixed a bug of `cc.PhysicsSprite` that `setIgnoreBodyRotation` function doesn't work.
5. Fixed a bug of `ccui.Button` that setting pressed texture doesn't work when scale9 enabled.
6. Fixed a bug of `ccui.ScrollView` that its `dir` property is null when passing `DIR_NONE` as `direction` in `_endRecordSlidAction` function.
Cocos2d-JS v3.3 RC0 @ Feb.1, 2015
* Added web exclusive functions: `_getFontStyle`, `_setFontStyle`, `_getFontWeight` and `_setFontWeight` APIs to `cc.LabelTTF`.
* Observed orientation change event on mobile for resolution policy adaptation.
* Bug fixes:
1. Fixed Cocos Studio JSON parser's issues for parsing nested animation.
2. Fixed Cocos Studio JSON parser's parameters parsing issues.
3. Fixed Cocos Studio JSON parser's issue for parsing layer.
4. Fixed Cocos Studio JSON action parser's issues.
5. Fixed Cocos Studio JSON parser's issue for parsing Scale9Sprite.
6. Fixed Cocos Studio JSON parser's issues caused by parsing process order.
7. Fixed Cocos Studio JSON parser's issue for parsing loading bar's direction.
8. Fixed UI layout system issues.
9. Fixed `cc.EditBox`'s position issue under certain resolution policies.
10. Fixed `ccui.ListView`'s issue for setting direction.
11. Fixed an issue of `cc.Tween` that its `_currentPercent` is incorrect in `updateHandler` function.
12. Fixed an issue of `ccui.Button` that its state is incorrect in `_onPressStateChangedToNormal`.
13. Fixed an issue of `cc.ArmatureAnimation`'s `setMovementEventCallFunc`.
14. Fixed an issue of `cc.Sequence` action when it's repeated.
15. Fixed `_anchorPointInPoints` usage issue.
16. Fixed an issue of `cc.GLProgram` that it doesn't work on some devices which didn't support highp float precision.
17. Fixed an issue of fade actions that they don't work when duration is 0.
18. Fixed `onended` callback issue of audio engine on iOS.
19. Fixed Cocos Builder's parser issue for auto playing animations.
20. Added a message to `ccs.Armature` that it doesn't support adding widget as its child.
21. Improved test cases for stability.
Cocos2d-JS v3.3 Beta @ Jan.24, 2015
* Added Cocos Studio v2.x parser and refactored 1.x parser.
* Upgraded new flow layout UI system in web engine.
* Refactored `load` events of texture2d, sprite and so on to be more intuitive.
* Added JavaScript file loader.
* Allowed set texture to null in `cc.Sprite`.
* Added full test cases for Cocos Studio v2.x parser and the new flow layout UI system.
* Upgraded MoonWarriors sample's UI and graphic design.
* Bug fixes:
1. Fixed a bug of Cocos2d UI, their focus event has been supported.
2. Fixed a buf of `ccui.Widget` that its percent position doesn't work.
3. Fixed a bug of `ccs.Armature` that its position doesn't update in visit on WebGL render mode.
4. Fixed a bug of `cc.Sprite` that its `setTextureRect` function doesn't work when `setColor` invoked.
5. Fixed a bug of `cc.PhysicsSprite` that its position is incorrect.
6. Fixed a bug of `ccs.Bone` that its `setOpacity` and `setColor` doesn't work.
7. Fixed a bug of `cc.LabelBMFont` that its word wrap doesn't work.
8. Fixed a bug of `cc.sys` that it gets the incorrect OS type when system is Linux.
9. Fixed a bug of `cc.audioEngine` that its loading path is incorrect.
10. Fixed a bug of `ccui.Widget` that it can't touch when it's reused.
11. Fixed a bug of UI system that the `setNormalizedPosition` doesn't work.
12. Fixed a bug of `cc.ActionInterval` that its `_times` conflict with `cc.Blink`.
13. Fixed release texture issue in canvas mode.
14. Fixed a bug of `ccs.actionManager` that its `getActionByName` doesn't work.
15. Fixed a bug of `cc.Sprite` that it can't draw without texture on WebGL mode.
16. Fixed a bug of `cc.audioEngine` that it doesn't work on baidu browser.
17. Fixed a bug of `cc.EditBox` that its position is incorrect on Canvas Mode and its string value is wrong when PlaceHolder is showing.
18. Fixed a bug of `cc.loader` that its `loadImg` function doesn't work when image is accessed cross origin.
19. Fixed a bug of `ccui.TextField` that its `contentSize` is incorrect in text field event.
Cocos2d-JS v3.2 @ Dec.29, 2014
* Replaced `transform` function with `setTransform` function under canvas render mode for better performance.
* Added a timer in `cc.audioEngine` to check audio element loading event, prevent the loading process being stucked when load audio file failed.
* Added some new browser types to `cc.sys`.
* Added some audio resource loading codes to ensure compatibility with WeChat browser.
* Added check for WebAudio support to ensure compatibility.
* Bug fixes:
1. Fixed an issue that `cc.InputManager` doesn't trigger touch event on chrome mobile emulator.
2. Fixed an issue that `cc.game.setFrameRate` doesn't work.
3. Fixed an issue that `cc.view` can't remove resize event listener.
4. Fixed an issue that `cc.EventManager` didn't set register flag to false when a listener is removed.
5. Fixed an issue that `cc.audioEngine` doesn't play some audios on some iOS devices.
6. Fixed an issue of ccui controls that their `setColor` doesn't work when cascade color is enabled.
7. Fixed an issue that `ccs.Armature`'s `setColor` doesn't work in canvas render mode.
8. Fixed an issue that `ccs.Armature` crashes when adding a child to it.
9. Fixed an issue that `cc.SpriteBatchNode`'s status is incorrect in WebGL render mode.
10. Fixed an issue of `cc.Layer` that its position is incorrect under bake mode.
11. Fixed an issue of `ccui.RichText` that its `setContentSize` doesn't work.
12. Fixed an issue of `cc.LabelTTF` that its `setColor` doesn't work when cascade color is enabled.
13. Fixed an issue of spine that its skeletons position is incorrect when scaleX equals to -1 and scaleY equals to 1.
14. Fixed `sp.Skeleton`'s API inconsistence by renaming `boundingBox` to `getBoundingBox`.
15. Removed all usages of deprecated create functions in the test cases.
Cocos2d-JS v3.2 RC0 @ Dec.11, 2014
* Refactoration of web engine by separating the render logic, the arthictecture level refactoration is now completed and brounght great performance improvement.
* Refactoration of web engine's resolution adaptation and audio engine with polyfilled adaptation logics for different devices and browsers. This ensures better compatibility and better extensibility for future needs.
* Added `setRotation` method to `ccui.ImageView`.
* Added a function that fill sprite with repeated texture in Canvas mode.
* Added `setLineHeight` method to `cc.LabelTTF`.
* Added `dumpAudioInfo` to `cc.audioEngine` for debugging purpose on mobile browser.
* Removed Cocos Studio's Protobuffer support from the framework.
* Added an outline shader sample.
* Bug fixes:
1. Fixed an issue of `cc.Sprite` that its rendering is incorrect without texture.
2. Fixed an issue of `cc.ClippingNode` that its stencil drawing is incorrect on Canvas Mode.
3. Fixed an issue of `TextFieldReader` that it will throw an error when 'areaWidth' and 'areaHeight' equal to zero.
4. Fixed an issue of `ccui.CheckBox` that its getSelectedState doesn't return its state.
5. Fixed an issue of `cc.LabelTTF` that it doesn't update the string when its string become to empty string.
6. Fixed an issue of `cc.ParticleSystem` that it can't change its texture mode and shape type in Canvas mode.
7. Fixed an issue of `cc.Layer`'s bake function that its position is incorrect when cc.view's scale isn't 1.
8. Fixed an issue of `ccs.ArmatureAnimation`'s `setMovementEventCallFunc` and `setFrameEventCallFunc`.
9. Fixed an issue of `console.log` that it isn't a funtion on IE9.
10. Fixed an issue of `CSLoader` that it will add duplicate resources to sprite frame cache.
11. Fixed an issue of `cc.ProgressTimer` that its setColor is not taking effect.
12. Fixed an issue of `cc.loader` that it will throw an error when loading a remote texture.
13. Upgrade html5 version chipmunk to the latest release.
Cocos2d-JS-v3.1 @ Oct.22, 2014
* Released Facebook Integration for Cocos2d-JS v1.0, all APIs have been significantly polished and stabilized. Improved test cases for Facebook with more features demonstrated.
* Upgraded Cocos2d-x to v3.3 rc0
* Supported Cocos Studio v2.0 including Timeline animation support and proto buffers format support for both web engine and JSB engine.
* Refactored load event of texture, sprite frame and sprite for better maintainability.
* Refactored `cc.rendererCanvas` for improving performance.
* Moved the `CC_Texture0` definition of fragment shader to cc.GLProgram to ensure compatibility with JSB.
* Added normalized position functions to cc.Node.
* Refactored the constructor of Cocos Studio's classes and deprecated all create functions.
* Refactored Cocos Studio reader for better maintainability.
* Improved Facebook SDK.
* Modified `cc.ProgressTo`'s behavior, its progression didn't reset to zero when the progression is 100.
* Changed `ccui.Widget`'s default anchor point to (0, 0) in widget reader.
* Removed all deprecated create function usage in engine and in the test cases.
* Bug fixes:
1. Fixed an issue of `cc.UILayout` that its scissor mode didn't work.
2. Fixed an issue of `ccui.TextBMFont` that its 'string' property setting was incorrect.
3. Fixed an issue of `cc.DrawNode` that its element's position was incorrect in Canvas mode.
4. Fixed an issue of `cc.Layer` that its bake function didn't work in new renderer.
5. Fixed an issue of `cc.Scale9Sprite` that its cached canvas size was incorrect.
6. Fixed an issue of `cc.Director` that its position was incorrect when calling `setProjection` in new renderer.
7. Fixed an issue of `cc.view` that the reinitialization logic of frame size was incorrect.
8. Fixed incorrect usage of `cc.progressTo` in progress action test.
9. Fixed an issue of CocosNodeTest for the new renderer.
10. Fixed minor issues in test cases.
* Known Issues:
1. `jsb.AssetsManager` doesn't work on windows due to a bug in libcurl
Cocos2d-JS v3.1 beta @ Oct.13, 2014
* Refactoration of the web engine with new renderer on the architecture level, optimization is under going.
* Released Facebook SDK for Cocos2d-JS beta2, its API have been significantly improved and stablized.
* Upgraded MoonWarriors sample with new set of graphical assets.
* Automatically enabled WebGL on iOS 8 safari.
* Upgraded chipmunk.js to the newest version.
* Supported setting color of shadow for `cc.LabelTTF`.
* Added `getTitleRenderer` function to ccui.Button.
* Supported Coco Studio timeline animation.
* Set the default value of LabelAtlas's `cascadeOpacityEnabled` and `cascadeColorEnabled` to true.
* Added a listener of texture to `cc.Sprite#setTexture` when the texture hasn't loaded.
* Activated `cc.pool` for all kind of objects.
* Added query test for chipmunk and added necessary JavaScript bindings.
* Bugs fix:
1. Fixed a bug of `cc.ComponentContainer` that a 'if' statement behavior is incorrect.
2. Fixed a bug of `cc.Scale9Sprite` that the behavior of Canvas and WebGL is different.
3. Fixed a bug of `cc.EventListener` that its pause state should set to true.
4. Fixed a bug of `cc.ParticleSystem` that it should apply canvas scaling on canvas rendering mode.
5. Fixed a bug of CCBoot.js that `cc.loader` should add a condition to check whether `crossOrign` property is undefined on IE9 and IE10.
6. Fixed a bug of `ccui.Widget` that its `setPosition` function's behavior is incorrect.
7. Fixed a bug of `ccui.LoadingBar` that its `barRenderer` should add to protected children array.
8. Fixed a bug of `cc.Texture2D` that its `TEXTURE_MAG_FILTER` should set to LINEAR.
9. Fixed a bug of `cc.TMXMapInfo` that its doesn't parse `rotation` property.
Cocos2d-JS-v3.0 Final @ Sep.10, 2014
* Facebook SDK Beta2: Added `appRequest` API.
* Facebook SDK Beta2: Added permission request in `login` API, removed `requestPermission` API.
* Facebook SDK Beta2: Renamed `request` API to `api`.
* Facebook SDK Beta2: Renamed `publishInstall` API to `activateApp`.
* Added getter and setter function for browser's density dpi: `cc.view.setTargetDensityDPI`, `cc.view.getTargetDensityDPI`.
* Added some type check functions.
* Added audio support for wechat browser.
* Added setPlaceHolderColor and setTextColor to ccui.TextField.
* Added API reference for Cocos Studio extension.
* Bugs fix:
1. Fixed an issue of `cc.Menu` that its item's touch priority is different than cc.eventManager.
2. Fixed an issue of `cc.view` that its NO_BORDER mode doesn't work correctly.
3. Fixed an issue of `cc.LabelBMFont` that its content size is different than JSB.
4. Fixed an issue of `cc.LabelBMFont` that its `setColor` is invalid on some mobile devices.
5. Fixed an issue of `cc.PageView` that it can't receive TOUCH_CANCEL event.
6. Fixed an issue of `cc.loader` that it can't load cross origin textures.
7. Fixed an issue that Facebook SDK Web's `appRequest` wraps info parameter incorrectly.
8. Fixed an issue of ccui widgets' `addEventListener` that it doesn't accept function's target as parameter.
Cocos2d-JS-v3.0 RC3 @ Aug.29, 2014
* Facebook SDK Beta: Unified the callback parameters for different platform.
* Facebook SDK Beta: Added payment API on Web platform.
* Facebook SDK Beta: Supported app request and share open graph API on Web platform.
* Facebook SDK Beta: Remove plugin configuration for Facebook SDK to simplify the usage.
* Facebook SDK Beta: Added test case for new features and improve all test cases.
* Cocos Console: Improved web compile with `--advanced` tag.
* Improved Cocos2d-JS inline docs to provide a better API reference document.
* Refactored cc.game for maintainability.
* Refactored cc.async to simplify and improve the usage.
* Added `cc.formatStr` for string formatting, for example: `cc.formatStr("a: %d, b: %b", a, b)`.
* Refactored cc.log to support formatted string.
* Refactored cc.pool's `hasObj` to `hasObject` and `removeObj` to `removeObject`.
* Added some state check to cc.audioEngine.
* Refactored sprite's blend function to support more features on Canvas.
* Refactored `cc.textureCache.textureForKey` to `cc.textureCache.getTextureForKey`, `cc.TMXTilemap#propertiesForGID` to `cc.TMXTilemap#getPropertiesForGID` to follow the standard API naming style.
* Detected mouse event on touch screen tablets.
* Support new construction for cc.PhysicsDebugNode and deprecated `cc.PhysicsDebugNode.create`
* Made cc.Texture2D's setTexParameters supports two types of parameters.
* Added test case for remote image loading.
* Bugs fix:
1. Fixed an issue of tilemap that it can't runAction in canvas render mode.
2. Fixed an issue of cc.eventManager that its removeListeners' codes are unreachable.
3. Fixed an issue of cc.EditBox that its position is incorrect.
4. Fixed an issue of cc.WebAudio that its stopped state is incorrect.
5. Fixed an issue of cc.audioEngine that it doesn't work on firefox after it compiled with advanced mode.
6. Fixed an issue of ccs.Bone that it doesn't update color and opacity correctly.
7. Fixed an issue of ccs.Armature that its setShaderProgram doesn't work.
8. Fixed cc.Sprite and cc.Scale9Sprite's issue so that their texture loads incorrectly.
9. Fixed an issue of ccui.LoadingBar that its setPercent is invalid.
10. Fixed an issue of Armature reader that it can't parse isTween property.
11. Fixed an issue of ccui.PageView that its getTouchBeganPosition returns incorrect value.
12. Fixed an issue of ccui.ImageView that its setColor doesn't work.
13. Fixed an issue of cc.RenderTexture that it doesn't support parameter depthStencilFormat.
14. Fixed an issue of ccs.ArmatureAnimation.setSpeedScale.
15. Fixed an issue of cc.Scale9Sprite that it has a line on iOS device.
16. Fixed CCProgressTimer draw on canvas with colorized sprite
17. Fixed an issue of cc.game that its frameRate setter is invalid.
18. Fixed an issue of cc.loader that its callback state is incorrect.
Cocos2d-html5-v3.0 RC2 @ Aug.8, 2014
* Refactored Cocos UI for more stable and friendly user experience.
* Upgraded Cocostudio reader to support version 1.2 - 1.5.x.
* Upgraded Cocostudio Armature animation from Cocos2d-x v3.2.
* Added back 2.x createWithXXX functions and deprecate all create/createWithXXX functions.
* Merged cc.NodeRGBA and cc.LayerRGBA to cc.Node.
* Fixed ctor functions bugs to support new construction.
* Refactored cc.Sprite's setColor to improve its performance.
* Renamed CCAffineTransform.js's functions to lowercase started functions.
* Upgraded cc.Scale9Sprite from Cocos2d-x 3.2.
* Improved cc.LabelTTF's line break algorithms to support multi-languages.
* Made cc.RenderTexture's beginWithClear accept color value from 0-255.
* Improved implementation of all Actions lower case alias creation functions.
* Added lower case creation functions for 3d actions and progress actions.
* Added cc.sys.platform API for detecting platform.
* Upgraded HelloWorld project with v3.0 APIs.
* Bugs fix:
1. Fixed a bug of cc.WebAudio that sourceNode's playbackState is invalid on some browsers.
2. Fixed a bug of cc.MenuItemToggle that callback is not correctly initialized when using new construction.
3. Fixed a bug of ccui.Layout that its clipping area is incorrect.
4. Fixed a bug of requestAnimFrame that it doesn't work after re-focus WeChat browser on Samsung mobile.
5. Fixed a bug of CCBoot.js that bind function is undefined in Safari for iOS 5.1.
6. Fixed a bug in cc.layer's bake function that its position is incorrect when cc.view is scaled.
7. Fixed a bug of cc.LayerMultiplex.
8. Fixed a bug of cc.TMXLayer that it can't display all map image when its type is hexagonal.
9. Fixed a transform error in ccs.TransformHelp.
10. Fixed a bug of cc.ControlSwitch.
11. Fixed image format constant inconsistence.
12. Fixed a bug of ccui.Widget that it is invisible after popScene.
13. Correct behavior of cc.TransitionSlideInB and cc.TransitionSlideInT.
14. Fixed bugs of ccui.Widget and ccui.Text's clone functions.
Cocos2d-html5-v3.0 RC0 @ July.3, 2014
* Added Facebook SDK plugin into Pluginx extension.
* Refactoration of gui system `ccui` for better performance, usage and maintainbility.
* Added `bake` function to `cc.Layer` to support layer baking.
* Added object pool extension: `cc.pool`.
* Added new easing functions: bezier action, quadratic actions, quartic actions, quintic actions, circle actions, cubic actions.
* Made `cc.loader` continue the counter process even if a resource failed to be loaded.
* Supported multiple property objects in `cc.Class.extend` function.
* Refactored `ccui.Widget`'s `getLeftInParent`, `getBottomInParent`, `getRightInParent`, `getTopInParent` to `getLeftBoundary`, `getBottomBoundary`, `getRightBoundary`, `getTopBoundary`.
* Refactored `cc.FadeIn.create(duration, toOpacity)` to `cc.FadeIn.create(duration)`.
* Refactroed all string access functions in `ccui` extension to `setString` and `getString`.
* Added `getContentSize` and `setContentSize` in `ccui` extension.
* Changed the default alpha value of `cc.Color` from `undefined` to 255.
* Made `cc.log` support formatted string.
* Bugs fix:
1. Fix bugs on creating sequence objcet or spawn object using new method.
2. Fix a bug that `ccui.LoadingBar`'s `setPercent` function will crash when its texture is in a plist file and scale9Enabled is true.
3. Fixed a bug of `cc.audioEngine` that it crashs when audio isn't correctly loaded and its duration is infinity.
4. Correction of the calculation of `cc.visibleRect`.
5. Fix `cc.Skin`'s bounding box calculation for canvas rendering.
6. Fix an issue that `cc.TextureCache` doesn't handle loaded texture in some case.
7. Fix an issue that texture rect could be zero sized in `initWithFile` function of `cc.Sprite`.
8. Fix a bug on inverted ClippingNode with DrawNode as stencil in Canvas render mode.
9. Fix a bug that `cc.SpriteFrame` didn't support initialization with texture name parameter.
10. Fix a bug on `ccs.ArmatureAnimation`'s loop parameter.
11. Fix a bug that `cc.JumpTo`'s `_delta` position calculation is incorrect.
12. Fix a bug of `cc._audioLoader` that it doesn't work when it failed to load an audio file.
Cocos2d-html5-v3.0 beta @ May.23, 2014
* Refactored actions to make it more friendly and easy-to-use.
* Integrated Spine skeleton animation feature.
* Renamed constants of ProgressTimer, Scale9Sprite, TMXLayerInfo, Node, ParticleSystem for maintainability.
* Modified mouseMove event behavior of cc.inputManager to compatible with Cocos2d-x
* Modified cc.game.run to receive a canvas id as parameter.
* Added local audio file playing from 'file://' origin.
* Added local images file displaying from 'file://' origin.
* Refactored cc.TMXLayer's setTileAt etc functions to support point or x,y as their parameters.
* Added a check to cc.Sprite and cc.SpriteFrame to avoid its texture rect out of bounds.
* Added a check to cc.SpriteFrame to avoid cc.loader release invalid sprite frame file.
* Made cc.Touch return copies of point.
* Made the default of cc.Color alpha value is 255 to avoid cc.Sprite's setColor is invalid.
* Optimized cc.Node.sortAllChildren for better performance.
* Added warning of cc.Texture2D if it has an invalid texture.
* Bugs fix:
1. Fixed a bug of cc.winSize that it returns incorrect value when using setDesignResolution.
2. Added a check to cc._setup to avoid double invocation.
3. Fixed a bug of cc.TMXMapInfo that its tile's property id is incorrect.
4. Fixed a bug of cc.Scale9Sprite that its CascadeColor and CascadeOpacity are invalid.
5. Fixed a bug of ccs.UILoadingBar which its barRendererScaleChangedWithSize is incorrect.
6. Added some forgotten files to build.xml for minimize core.
7. Corrected a mistake of renderMode default value in CCBoot.js.
8. Fixed a bug of ccui.Layout's draw function that its scaleX, scaleY value is incorrect.
9. Fixed a bug of cc.Audio's stopMusic function.
10. Fixed a bug of TextureCache that it can't remove image's event handler.
11. Fixed ClippingNode's DrawNode stencil bug on Canvas.
12. Fixed a typo 'cc.radiansToDegress' function to 'cc.radiansToDegrees'.
13. Fixed a bug of ccui.ImageView that its setSize is invalid when the picture without pre-load.
14. Fixed a bug of cc.ParticleSystem that it throws a error when create from CocosBuilder.
15. Fixed a bug of cc.LabelAtlas that it can't display its children.
16. Fixed a bug of cc.fontLoader that it can't load custom font.
17. Fixed a bug of ccui.Widget that its setOpacity is invalid.
18. Fixed a bug of cc.Node that it transform value is incorrect when a node skew to a special value.
Cocos2d-html5-v3.0 alpha2 @ April.14, 2014
* Minimized the size of core from 254k to 113k after google closure advanced compiling
* Made cc.DrawNode support some DrawingPrimitive's drawing function on WebGL mode
* Added undefined checking in cc.loader for better performance.
* cc.Sprite supports creating a sprite through external URL.
* Added the warning information to notice developers that their project.json cannot be loaded or parsed.
* Added retina display support to cc.Editbox.
* cc.Node's pauseSchedulerAndActions and resumeSchedulerAndActions are deprecated, please use pause and resume instead.
* Added render mode checking to 3D action classes.
* Added SocketIO
* Sync cc.eventManager to the latest version of Cocos2d-x v3.0 Stable.
* ccui.Layout's doLayout function has been set to private function "_doLayout"
* Made actions extendable directly via ctor
* Added null callback check in cc.textureCache.addImage
* Fixed the API inconsistence of ccs.ArmatureAnimation.play
* Fixed compatibility and performance for ctor
* Renamed all Uppercase functions to lowercase in CCMacro
* Added necessary GL constants in H5
* Fixed CONSTANTS inconsistence between h5 and JSB
* Bugs fix:
1. Fixed ccs.comAttribute API incompatible issue
2. Fixed a bug of Cocostudio's data reader that getting isTween value is incorrect when the attribute value is false.
3. Fixed a bug of Sprite that it doesn't work when its texture doesn't preload and its parent is a SpriteBatchNode
4. Fixed a bug in CCBoot.js that console.error is invalid on firefox.
5. Fixed some comment errors of framework.
6. Fixed a bug of cc.LabelBMFont that it's multiline works incorrectly.
7. Fixed a bug that Touches event doesn't work in release mode on IE browser.
8. Fixed a bug that cc.winSize has not been reset after cc.view.setDesignResolutionSize.
9. Fixed typo in ccui.Widget.TOUCH_BEGAN
10. Fixed a bug of cc.MenuItemSprite.create that
11. Fixed a bug of cc.loader that it need to set value before call the callback.
12. Fixed a bug of cc.log that it doesn't work in IE9
13. Fixed IE incompatible issue with __lookupGetter__
14. Fixed a mistake of cc.Node that it return a reference of _position in getPosition
15. Fixed a bug of cc.ClippingNode that its _super is undefined
16. Fixed a bug of inputManager's touch event in IE browser
* Known Issues:
1. EventListener is not extendable.
Cocos2d-html5-v3.0 alpha @ March.15, 2014
* Refactor some properties of all rendering classes with getter setter for providing javascript user friendly APIs.
* Provide `attr` function for cc.Node and its descendants to permit modify multiple properties at the same time with a key-value object.
* Refactor foundational data structures for better maintainability.
* Add event manager to cocos2d-html5, all events are dispatched via cc.eventManager to event listener.
* Refactor cc.Application to cc.game.
* Refactor singleton Classes to javascript object.
* Refactor all createWithXXX functions into unified create function with different parameters.
* Use `moduleConfig.json` to config the paths of engine scripts.
* `cocos2d.js` is replaced with `project.json`.
* Refactoring cc.loader.
* CocoStudio GUI updated to 3.0, and ccs prefix of UI widgets have been changed to ccui.
* CocoStudio v1.3.0 has been supported in v3.0.
* richText has been supported in v3.0.
* Use `cc.BuilderReader.registerController` to register controller of ccb.
* Add `cc.path` to handle operations of file path.
* Add `cc.async` to handle async operations.
* Add cc.NodeGrid in v3.0.
* Replace `replaceWithScene` and `runWithScene` with `runScene`.
* move sys.xxx to cc.sys.xxx.
* Refactor CCEGLView.js for better maintainability.
* Refactor CCScheduler.js for better maintainability.
* Remove arguments.callee which is forbidden in ECMAScript strict mode.
* Refactor Array clean function for better performance.
* Refactor some functions about array operation.
* Refactor FadeIn/FadeOut to fix a bug that it always start from/to 255.
* Rewrite functions in CCNS.js with regex.
* Move CCFormatHelper and CCNS content into CCCommon.js.
* Refactor cc.Screen to support all browsers.
* Add retina display support for Apple devices to cc.view.
* Add "allLayers" function to cc.TMXTiledMap.
* Make cc.p and cc.size support two types of parameters.
* cc.DrawNode supports all functions of cc.DrawingPrimitive on Canvas mode.
* WebAudioEngine is supported on iOS now.
* Use event on cc.canvas to make full screen.
* Add a browser white list that support multiple audio playback at the same time.
* Removed in/hasOwnProperty usage in engine for better performance.
* Refactoring CCCommon.js, delete some unused functions, rename some functions for better maintainability.
* Add analytics plugin protocol ,Flurry plugin and ProtocolAds.js plugin protocol.
* Arguments length check replaced by undefined check for better performance.
* Fix legacy Function.prototype.bind support.
* Bugs fix:
1. Avoid CCLabelTTF enter in infinite loop while character's width larger than the dimension width
2. Add jsDoc Flags to cc.NodeRGBA and cc.LayerRGBA
3. Fixed a bug that Schedule doesn't restart when widget is re-added after being removed
4. Correction of split logic in CCLabelTTF
5. Fixed a bug that armature animation does not display correctly on canvas mode
6. Correct gui widget clone functions
7. Fixed a bug of cc.SpriteFrameCache that filePath is needed in `loadedFileNames`
8. Add a condition check to avoid texture out of range bug
9. Fixed a bug of cc.Editbox that its position is incorrect when its parent node isn't root node.
10. Fixed a SimpleAudioEngine's state error.
11. Fixed a bug of cc.TMXTileMap that its `_tileProperties` should be a dictionary object
12. Fixed a bug of cc.DrawNode that it need to deep-copy verts in `drawPoly`
13. Fixed a bug of UILabelBMFont that variable `_strStringValue` should be `_stringValue`
14. Fixed a bug in SceneReader's `setPropertyFromJsonDict` function
15. Fixed a bug when margin not set in ccs.Margin
16. Fixed a bug of cc.TMXLayer that its `removeChild` works incorrectly.
* Known Issues:
Cocos2d-html5-v2.2.2 @ Dec.31, 2013
* Resolution policy now act as a combination of cc.ContainerStrategy and cc.ContentStrategy so that user can beautifully customize its behavior.
* cc.LabelTTF's now support perfectly automatic line break with occidental and Chinese characters.
* cc.ClippingNode for canvas render mode is implemented.
* Refactored cc.Node and cc.Sprite by adding cc._PointConst and cc._SizeConst for better Performance. Now the performance of setPosition and getPosition is faster 65% than before.
* CCNode's setContentSize and setAnchorPoint support two types of parameters, more friendly and more efficient. setAnchorPoint(x,y) is faster 35% than setAnchorPoint(cc.p(x,y)).
* Added NPM support and adjusted folder structure. It supports modules customization, the mini HelloWorld is just 185KB when package all files into single file.Please visit NPM Guide for more details.
* Added SpriteFrameCache JSON format support.
* Added source map generating of Closure Compiler advance mode , please make sure your JDK version is 7.0 and up.
* Improved audio compatibility for mobile browser, added playing queue to solve the one audio restriction of some mobile browser.
* Refactoring TMXLayer for better performance.
* set cc.Rect's origin and size from public to private for compatibility with JSB.
* CocoStudio supports async image loading.
* cc.log supports printing object content to console
* Refactoring indexing of actionManager and Scheduler for better performance.
* ClippingNode supports some features on Canvas Mode.
* Migrated Armature to v2.2.2.
* Add callback function to CocoStudio action completion and refactoring it for better performance.
* CCBReader supports that CCControl can send action by all types of event.
* Add create function to cc.NodeRGBA
* Add jsdoc document to CocoStudio classes
* Bugs fix:
1. Fixed a bug of TMXLayer that it has thin lines at tile's border when EGLView's scale doesn't equal 1.
2. Fixed bugs of LabelBMFont about updateDisplayedOpacity and multi-line is incorrect.
3. Fixed a bug of LabelTTF that enter an infinite loop when setting special string and fontSize to it.
4. Fixed a bug of NodeRGBA and LayerRGBA about updateDisplayedColor and updateDisplayedOpacity.
5. Fixed a bug of ProgressTimer that it can't change color and opacity when calling setColor and setOpacity directly.
6. Fixed a bug of cc.ProgressTimer that it has a blink when its reverseDirection equals false and type equals cc.PROGRESS_TIMER_TYPE_RADIAL.
7. Some Loaders need modify their default value to adapt CocosBuilder that CocosBuilder ignores some two properties object like cc.Point when all the properties equals to zero.
8. Fixed a bug of Fixed a bug of TMXTileMap that its getProperty doesn't work.
9. Fixed a bug of ActionInterval that it throws error when its target doesn't have RGBAProtocol property.
10. Fixed a bug of MenuItemSprite that it throws an error when create a MenuItemSprite object with cc.Node.
11. Fixed a bug of UIWidget that its container intercept touch event while they can't.
12. Fixed a bug of ccs.UILayout about relative positioning.
13. Fixed a bug of Armature that its nodeToParentTransformCanvas correct.
* Known Issues:
1. Effect Advanced Lens3D doesn't work
2. ClipNodeTest effects varies in different browsers
3. Stencil of cc.ClippingNode doesn't work well with WEBGL render mode, the stencil have the right size and shape but it masks the content with a monochrome mask.
Cocos2d-html5-v2.2.1 @ Nov.19, 2013
* CocoStudio is now supported on Cocos2d-html5. The GUI, scene and component modules have been added to it.
* cc.EGLView and most render classes have been re-written to adapt multiple resolution resources in-order to optimize performance on mobile browsers.
* Refactored cc.LabelTTF, now its contentSize and position is correct for labels which has defined stroke and shadow.
* Corrected the behavior of "CascadeColorEnabled" and "CascadeOpacityEnabled" for cc.NodeRGBA and cc.LayerRGBA.
* All cc.Assert has being replaced, and more arguments checking and log information have added to engine's function.
* Added cc.Screen to engine, it uses to enter/exit FullScreen mode.
* Added cc.VisibleRect to engine, it provides nine points of game view for positioning.
* cc.WebAudioEngine now works perfectly on chrome.
* CocoStudio's namespace changes to 'ccs' now, and the other module's namespace will be renamed and support NPM in next version.
* cc.rect now accepts more types of parameters on JSB and HTML5 now, for example: cc.rect(1,1,1,1) and cc.rect(aRect), and cc.rect(cc.p(1,1),cc.size(10,10));
* Optimized cc.Node's getBoundingBoxToWorld for better Performance.
* Modified the _sequenceCompleted method in CCBAnimation, it can set the next sequence in callback now.
* Improved the maintainability for _drawSceneForWebGL and _drawSceneForCanvas.
* ParticleExamples has been refactored for JSB.
* HelloHTML5World's CircleSprite has been removed, because it doesn't work on JSB.
* Bugs fix:
1. Fixed a Scale9Sprite's bug that setCapInsets is invalid.
2. Fixed a bug that prevents the game to run on Chrome 31 WebGL mode.
3. Fixed a bug of LabelTTF that doesn't work on Baidu browser.
4. Fixed a bug of Sprite that it shouldn't to set transform dirty when setting color or opacity.
5. Fixed a bug that cc.EditBox's setFontSize is invalid.
6. Fixed a bug that Particles doesn't work when search path in FileUtils was set.
7. Fixed a bug of Scale9Sprite that it throws an error when _scale9Image is null.
8. Fixed a bug of LayerGradient that it shows wrong size when setting content size.
9. It should listen to the method "onLoad" in cc.FileUtils when the browser isn't IE.
* Known Issues:
1. Effect Advanced Lens3D doesn't work
2. ClipNodeTest effects varies in different browsers
Cocos2d-html5-v2.2 @ Sep.19, 2013
* Improved Sprite, Node, LabelTTF class define from separated code to combined code for maintainability, now it is clean and clear
* added a new sample game "Fruit attack" which works great on PC browsers, mobile browsers, and can even be run natively as an android and iOS app with JSB
* cc.Sprite and its subClasses's texture has been replaced from DOM element to cc.Texture2D on Canvas mode
* Improved cc.Texture2d for direct using without pre-loading image resources, you don't need to wait resources loading when create a new scene or layers
* Migrated CCBReader and GUI to Cocos2d-x 2.1.4
* Improved update function of Action, and avoid using temporary object, it is good for GC and performance
* Modified LabelTTF's rendering from direct drawing to pre-rendering for performance, 100% faster than before on mobile browser
* Fixed APIs of HTML5 according to JSB for compatibility, e.g. cc.ParticleSystemQuad has merged into cc.ParticleSystem, please check it on upgrade guide v2.1.5 to v2.2(http://www.cocos2d-x.org/wiki/Upgrade_Guide_from_Cocos2d-html5_v215_to_v22)
* Added Hiding url address bar for mobile browser, please check the template and hello world
* Re-writed Canvas Mode of RenderTexture to adapt WebGL interface
* Added frame event, collider and blend type supporting for Armature. Now Armature supports two tools:1.CocoStudio(windows,http://www.cocostudio.org),2.DragonBones(flash, https://github.com/2youyouo2/SkeletonAnimationDesignPanel)
* Set auto render mode default value to canvas in mobile browsers and WebGL in desktop browsers
* Bug fix:
1. Fixed cc.Sprite's displayFrame returns wrong value on Canvas mode.
2. Fixed cc.LabelBMFont is very slow when calling setString
3. Fixed a bug of CCBReader that cc.ControlButton doesn't work when its controller is _jsControlled
4. Fixed a bug of cc.TextureCache that the status of texture is wrong in callback
5. Fixed a bug of cc.Scale9Sprite that its contentSize is wrong when call setCapInsets
6. Fixed a bug of cc.TableView's that contentSize is wrong when change datasource
7. Fixed a bug of cc.Sprite that its children also follow fliped when it was fliped
8. Fixed cc.Node's nodeToWorldTransform returns wrong value on Canvas Mode
9. Fixed a bug of cc.LayerColor that represent incorrect opacity passed into init method
10. Stop listening and remove the event for HtmlImageElement object onload
11. Fixed cc.ProgressTimer display wrong when its sprite was flipped
12. Fixed some bugs for actions that set their object property through reference when initiating actions.
* Known Issues:
1. Effect Advanced Lens3D doesn't work
2. ClipNodeTest effects varies in different browsers
Cocos2d-html5-v2.1.5 @ July.24, 2013
* Ported engine API to keep the same as Cocos2d-x v2.1.4 API
* Optimized John Resig's inheritance pattern (cc.Class.extend) with advanced property initialization.
* Implemented the rest of extensions features according to Cocos2d-x v2.1.4
* Integrated Armature module
* Rewrote CCGrid, CCMotionStreak , CCProgressTimer with TypeArray
* Optimized performance for actions
* Optimized performance for MoonWarriors and CocosDragonJS
* Bug fix:
1. Fixed cc.EditBox Dom Element position issue when EGLView is set
2. Fixed cc.EGLView adjustSize bug
3. Fixed the bug of cc.ParticleBatchNode that it doesn't hide particles after particle life has expired when calling stopSystem()
4. Fixed a bug that LabelTTF dimension behavior doesn't support height=0
5. Fixed line height for multiline LabelTTF and overlapping pixels in Scale9Sprite on Canvas browsers
6. Fixed a bug of cc.SimpleAudioEngine that unloading effect doesn't work
* Known Issues:
1. Effect Advanced Lens3D doesn't work
2. ClipNodeTest effects varies in different browsers
3. nodeToParentTransform in cc.Node returns wrong value on Canvas mode
Cocos2d-html5-v2.1.4 @ Jun.12, 2013
* Added support for multiple resources loading. This mechanic is the same as cocos2d-x now
* Optimised "Performance Tests -> Sprites Test", and increased its benchmark to 220%!
* Migrated audio (CocosDenshion) API to keep the same as Cocos2d JS API
* Added auto test for NodeTests and TilemapTests
* Changed CCTextureCache member functions such as addImage(path), addImageAysnc(path), removeTextureForKey(key) from using relative path to absolute path
* Added support for particle batch node
* Bug fix:
1. Fixed preLoading issue on iOS 5.1.1
2. Fixed cc.Menu / cc.MenuItemImage remaining touchable issue after replaceScene
3. Fixed Box2d and chipmunk path error for single engine file mode
4. Fixed cc.EditBox Dom Element position issue when cc.EditBox skewed
5. Fixed cc.ScrollView position issue when it's parent node moved
6. Fixed cc.TouchDispatcher can't touch issue when WebPage has been scrolled on Firefox or IE
* Known Issues:
1. Effect Advanced Lens3D doesn't work
2. ClipNodeTest effects varies in different browsers
Cocos2d-html5-v2.1.3 @ May.1, 2013
* CCEditbox now implemented for WebGL and JSB
* Updated CCBReader to latest version
* Performance optimization on Firefox 20%
* Added render mode flag to switch between WebGL and canvas2d
* Added support for Tizen
* Now able to load embedded texture file in a plist
* EGLView now works if canvas is placed inside another DOM element
* Added a Simulator which can be found in MoonWarriors Directory
* Bug fix:
1. Preloading on some mobile browsers
2. CCLoader for WebGL
3. ccNode memory leak
* Known Issues:
1. Effect Advanced Lens3D doesn't work
2. Particle System has some weird behavior when load from CCBReader
3. RenderTextureTest RenderTextureIssue937 & Issue1464 doesn't work properly
4. ClipNodeTest effects varies in different browsers
Cocos2d-html5-v2.1.2-beta @ Mar.20, 2013
* WebGL rendering mode implemented - blazing fast on supported browsers
* Added many WegGL test to testbed
* cc.Loader now supports multiple stage preloading - all tests now preload by multi resource groups
* Now warns the user if their browser does not support html5
* cc.Node now uses transform matrix - better performance
* Accelerometer implemented - Also works on Javascript binding (JSB) for Cocos2d-x & Cocos2d-iPhone
* Supports MP4 and M4a Audio format now
* Designer resolution for multi resolution support - Also works on Javascript binding for Cocos2d-x
* Faster Particle - thanks to Ivo Wetzel
* Bug fixes:
1. File utility fixed
2. Audio support bug fixed
3. Removed some trailing coma which prevents blocks closure compiler
4. Local storage bug fixed
5. cc.MenuItemImage and cc.MenuItemToggle bugs fixed
6. Fixed compatibility with some UIWebView
7. Fixed rounding errors on ease actions
* Known Issues:
1. Effect Advanced Lens3D doesn't work
2. particle system can't load texture from plist
3. EditBox doesn't work on WebGL mode
4. Particle System has some weird behavior when load from CCBReader
5. RenderTextureTest RenderTextureIssue937 & Issue1464 doesn't work properly
6. ClipNodeTest effects varies in different browsers
Cocos2d-html5-v2.1.1 @ Jan.28, 2013
* Fixed bugs
* Added mouse button to MouseDispatcher, supports right-click
* Changed preload audio type from "bgm" and "effect" to "sound"
* Added "Sys" class for system capabilities
* Improved cc.BuilderReader to support .ccbi extension auto-completion
* Improved TMXXMLParser to support XML, CSV and zlib compression
* Changed cc.Time.gettimeofdayCocos2d to Date.now which is more javascript friendly.
* Added support for stackable actions
Cocos2d-html5-v2.1.0 @ Dec.4, 2012
* Improved cc.Class and add Release Mode
* All tests and games can now be run on Cocos2d-html5, Cocos2d-iPhone and Cocos2d-x
* Added support for google.base
* Added support for CocosBuilder and Bone Animation
* Updated API for Javascript Binding
* Integrated Chipmunk physical engine and chipmunk tests
* Added physicsDebugNode, physicsSprite, drawNode
* Built cocos2d-js-tests repo for tests
* Fixed support for mouse/touch/keyboard
* Fixed bugs
* Added WaterMelon with me and CocosDragon games for sample
* Added Edit Box for input
Cocos2d-html5-v2.0.0 @ Aug.28, 2012
* Updated API to Cocos2d-x V2.0
* Updated template and directory name
* Improved JS files loader
* Added support for Dom rendering
* Updated JSDoc comments and shell
* Added TileMap property process and flip
* Improved BMFont
* Added Actions spline paths and cc.AnimationFrame
* Added support for multi-touch
* Added mini-framework for Dom manipulation
* Changed cc.Animation, cc.AudioEngine, cc.LableTTF and cc.Sprite API
Cocos2d-html5-v0.5.0-alpha2 @ Jun.18, 2012
* Changed API, use "create" to construct all objects
* Fixed naming of variables
* Added JSDoc comments and shell
* Fixed Dom Menu flicker bug
* Changed code for closure compiler Advance optional
* Added version control
Cocos2d-html5-v0.5.0-alpha @ May.28, 2012
* supports canvas and Dom Menu
* part of test cases were added and tested in chrome
* porting from cocos2d-x is not finished
* files must load from http server
Cocos2d-html5-v0.1.0 @ Jan.29, 2012
* Build the directory structure of Engine
* cocos2d-html5 first version
* more details: http://www.cocos2d-x.org/

2819
engine/EngineErrorMap.md Normal file

File diff suppressed because it is too large Load Diff

10
engine/MAINTAINERS Normal file
View File

@@ -0,0 +1,10 @@
2youyou2
dabingnn
jareguo
knoxHuang
nantas
natural-law
pandamicro
VisualSJ
zilongshanren

153
engine/README.md Normal file
View File

@@ -0,0 +1,153 @@
<p align="center">
<a href="https://www.cocos.com/">
<img src="https://user-images.githubusercontent.com/1503156/50446380-ad88c980-094f-11e9-8eff-0094bde708d0.png">
</a>
</p>
<p align="center">
<a href="https://github.com/cocos-creator/engine/stargazers">
<img src="https://img.shields.io/github/stars/cocos-creator/engine.svg?style=flat-square&colorB=4183c4"
alt="stars">
</a>
<a href="https://github.com/cocos-creator/engine/network">
<img src="https://img.shields.io/github/forks/cocos-creator/engine.svg?style=flat-square&colorB=4183c4"
alt="forks">
</a>
<a href="https://github.com/cocos-creator/engine/releases">
<img src="https://img.shields.io/github/tag/cocos-creator/engine.svg?label=version&style=flat-square&colorB=4183c4"
alt="version">
</a>
<a href="./licenses/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square&colorB=4183c4"
alt="license">
</a>
<a href="https://twitter.com/cocos2dx">
<img src="https://img.shields.io/twitter/follow/cocos2dx.svg?logo=twitter&label=follow&style=flat-square&colorB=4183c4"
alt="twitter">
</a>
<a href="https://gitpod.io/from-referrer/">
<img src="https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod" alt="Gitpod Ready-to-Code" />
</a>
</p>
# Cocos Creator
![2.2.0 Main Window](https://user-images.githubusercontent.com/1503156/67261891-3cfdfb00-f4d5-11e9-9b2d-15ff2cb015f4.png)
Cocos Creator is a complete package of game development tools and workflow, including a game engine, resource management, scene editing, game preview, debug and publish one project to multiple platforms. Cocos Creator focused on content creation, which has realized features like thorough scriptability, componentization and data driven, etc. on the basis of Cocos2d-x. With JavaScript, you can scripting your component in no time. The editor and engine extension is also made with JavaScript so you can make games and refine your tool in a single programming language. Cocos Creator is an provides an innovative, easy to use toolset such as the UI system and Animation editor. The toolset will be expanding continuously and quickly, thanks to the open editor extension system.
This repo is the engine framework for Cocos Creator. Cocos Creator's in-editor scene view and web runtime share the same framework, which is the content of this repo. It's originally forked from [Cocos2d-html5](https://github.com/cocos2d/cocos2d-html5/), we build up an Entity Component architecture on it to meet the needs of Cocos Creator.
This framework is a cross-platform game engine written in JavaScript and licensed under MIT. It supports major desktop and mobile browsers, it's also compatible with [Cocos2d Javascript Binding engine](https://github.com/cocos-creator/cocos2d-x-lite) to support native platforms like iOS, Android, Win32, macOS.
The framework is naturally integrated with Cocos Creator, so it's not designed to be used independently.
## Developer
### Prerequisite
- Install [node.js v8.0.0+](https://nodejs.org/)
- Install [gulp-cli v3.9.0+](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md)
### Install
In cloned project folder, run the following command to setup dev environment:
```bash
# Initialize gulp task dependencies
# npm is a builtin CLI when you install Node.js
npm install
```
This is all you have to do to set engine development environment.
### Build
```bash
gulp build
```
If the compilation process encounters a "JavaScript heap out memory" warning, you can use the following command line
```bash
gulp build --max-old-space-size=8192
```
### Test
#### Prerequisite
- Install [express](http://expressjs.com/): `npm install express`.
- Install gulp-qunit: `npm install gulp-qunit`.
#### Unit Test
- Test in CLI
```bash
npm test
```
- Test in browser
1. Build for testing.
```bash
gulp build-test
```
2. Start express in cloned project folder.
```bash
node test/qunit/server.js
```
3. Open [http://localhost:8511/bin/qunit-runner.html](http://localhost:8511/bin/qunit-runner.html) in your browser.
### Online one-click Setup
You can use Gitpod(an online IDE which is free for Open Source) for developing the project online. With a single click it will launch a workspace and automatically:
- clone the `Cocos Creator` repo.
- install all the dependencies mentioned above.
- run `gulp build`, `npm test` in separate terminals.
- start the express server and open [http://localhost:8511/bin/qunit-runner.html](http://localhost:8511/bin/qunit-runner.html) in the right corner of the IDE.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/from-referrer/)
### DebugInfos
View [EngineErrorMap.md](https://github.com/cocos-creator/engine/blob/master/EngineErrorMap.md)
All the debug infos are defined in file EngineErrorMap.md.
The file DebugInfos.json will be generated based on EngineErrorMap.md, when run `gulp build` command.
For details below:
1. Define log in EngineErrorMap.md
example:
```
### 1001
cocos2d: removeAction: Target not found
```
2. Define deprecated log in EngineErrorMap.md
The log should be marked as DEPRECATED when then logId is no longer referenced in the project.
example:
```
### 1000
<!-- DEPRECATED -->
cc.ActionManager.addAction(): action must be non-null
```
## Useful links
* [Official site](https://www.cocos.com/products#CocosCreator)
* [Download](https://www.cocos.com/creator)
* [Documentation](https://docs.cocos.com/creator/manual/)
* [API References](https://docs.cocos.com/creator/api/)
* [Forum](https://discuss.cocos2d-x.org/c/creator)

32
engine/api.d.ts vendored Normal file
View File

@@ -0,0 +1,32 @@
declare let CC_JSB: boolean
declare let CC_NATIVERENDERER: boolean
declare let CC_EDITOR: boolean
declare let CC_PREVIEW: boolean
declare let CC_TEST: boolean
declare let CC_DEBUG: boolean
declare let cc: {
// polyfills: {
// destroyObject? (object: any): void;
// };
[x: string]: any;
}
declare let Editor: any;
// https://medium.com/dailyjs/typescript-create-a-condition-based-subset-types-9d902cea5b8c
type FlagExcludedType<Base, Type> = { [Key in keyof Base]: Base[Key] extends Type ? never : Key };
type AllowedNames<Base, Type> = FlagExcludedType<Base, Type>[keyof Base];
type KeyPartial<T, K extends keyof T> = { [P in K]?: T[P] };
type OmitType<Base, Type> = KeyPartial<Base, AllowedNames<Base, Type>>;
type ConstructorType<T> = OmitType<T, Function>;
declare interface IWritableArrayLike<T> {
readonly length: number;
[index: number]: T;
}
declare let module: {
exports: object
}

View File

@@ -0,0 +1,582 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
require('../core/platform/CCClass');
const misc = require('../core/utils/misc');
/**
* @module cc
*/
/**
* !#en Base class cc.Action for action classes.
* !#zh Action 类是所有动作类型的基类。
* @class Action
*/
cc.Action = cc.Class({
name: 'cc.Action',
//**************Public Functions***********
ctor:function () {
this.originalTarget = null;
this.target = null;
this.tag = cc.Action.TAG_INVALID;
},
/**
* !#en
* to copy object with deep copy.
* returns a clone of action.
* !#zh 返回一个克隆的动作。
* @method clone
* @return {Action}
*/
clone:function () {
var action = new cc.Action();
action.originalTarget = null;
action.target = null;
action.tag = this.tag;
return action;
},
/**
* !#en
* return true if the action has finished.
* !#zh 如果动作已完成就返回 true。
* @method isDone
* @return {Boolean}
*/
isDone:function () {
return true;
},
// called before the action start. It will also set the target.
startWithTarget:function (target) {
this.originalTarget = target;
this.target = target;
},
// called after the action has finished. It will set the 'target' to nil.
stop:function () {
this.target = null;
},
// called every frame with it's delta time. <br />
step:function (dt) {
cc.logID(1006);
},
// Called once per frame. Time is the number of seconds of a frame interval.
update:function (dt) {
cc.logID(1007);
},
/**
* !#en get the target.
* !#zh 获取当前目标节点。
* @method getTarget
* @return {Node}
*/
getTarget:function () {
return this.target;
},
/**
* !#en The action will modify the target properties.
* !#zh 设置目标节点。
* @method setTarget
* @param {Node} target
*/
setTarget:function (target) {
this.target = target;
},
/**
* !#en get the original target.
* !#zh 获取原始目标节点。
* @method getOriginalTarget
* @return {Node}
*/
getOriginalTarget:function () {
return this.originalTarget;
},
// Set the original target, since target can be nil.
// Is the target that were used to run the action.
// Unless you are doing something complex, like cc.ActionManager, you should NOT call this method.
setOriginalTarget:function (originalTarget) {
this.originalTarget = originalTarget;
},
/**
* !#en get tag number.
* !#zh 获取用于识别动作的标签。
* @method getTag
* @return {Number}
*/
getTag:function () {
return this.tag;
},
/**
* !#en set tag number.
* !#zh 设置标签,用于识别动作。
* @method setTag
* @param {Number} tag
*/
setTag:function (tag) {
this.tag = tag;
},
// Currently JavaScript Bindigns (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
// and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
// This is a hack, and should be removed once JSB fixes the retain/release bug.
retain:function () {
},
// Currently JavaScript Bindigns (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
// and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
// This is a hack, and should be removed once JSB fixes the retain/release bug.
release:function () {
}
});
/**
* !#en Default Action tag.
* !#zh 默认动作标签。
* @property TAG_INVALID
* @constant
* @static
* @type {Number}
* @default -1
*/
cc.Action.TAG_INVALID = -1;
/**
* !#en
* Base class actions that do have a finite time duration. <br/>
* Possible actions: <br/>
* - An action with a duration of 0 seconds. <br/>
* - An action with a duration of 35.5 seconds.
*
* Infinite time actions are valid
* !#zh 有限时间动作,这种动作拥有时长 duration 属性。
* @class FiniteTimeAction
* @extends Action
*/
cc.FiniteTimeAction = cc.Class({
name: 'cc.FiniteTimeAction',
extends: cc.Action,
ctor:function () {
//! duration in seconds
this._duration = 0;
},
/**
* !#en get duration of the action. (seconds).
* !#zh 获取动作以秒为单位的持续时间。
* @method getDuration
* @return {Number}
*/
getDuration:function () {
return this._duration * (this._timesForRepeat || 1);
},
/**
* !#en set duration of the action. (seconds).
* !#zh 设置动作以秒为单位的持续时间。
* @method setDuration
* @param {Number} duration
*/
setDuration:function (duration) {
this._duration = duration;
},
/**
* !#en
* Returns a reversed action. <br />
* For example: <br />
* - The action will be x coordinates of 0 move to 100. <br />
* - The reversed action will be x of 100 move to 0.
* - Will be rewritten
* !#zh 返回一个新的动作,执行与原动作完全相反的动作。
* @method reverse
* @return {Null}
*/
reverse:function () {
cc.logID(1008);
return null;
},
/**
* !#en
* to copy object with deep copy.
* returns a clone of action.
* !#zh 返回一个克隆的动作。
* @method clone
* @return {FiniteTimeAction}
*/
clone:function () {
return new cc.FiniteTimeAction();
}
});
/**
* @module cc
*/
/*
* Changes the speed of an action, making it take longer (speed > 1)
* or less (speed < 1) time. <br/>
* Useful to simulate 'slow motion' or 'fast forward' effect.
*
* @warning This action can't be Sequenceable because it is not an cc.IntervalAction
* @class Speed
* @extends Action
*
* @param {ActionInterval} action
* @param {Number} speed
*/
cc.Speed = cc.Class({
name: 'cc.Speed',
extends: cc.Action,
ctor:function (action, speed) {
this._speed = 0;
this._innerAction = null;
action && this.initWithAction(action, speed);
},
/*
* Gets the current running speed. <br />
* Will get a percentage number, compared to the original speed.
*
* @method getSpeed
* @return {Number}
*/
getSpeed:function () {
return this._speed;
},
/*
* alter the speed of the inner function in runtime.
* @method setSpeed
* @param {Number} speed
*/
setSpeed:function (speed) {
this._speed = speed;
},
/*
* initializes the action.
* @method initWithAction
* @param {ActionInterval} action
* @param {Number} speed
* @return {Boolean}
*/
initWithAction:function (action, speed) {
if (!action) {
cc.errorID(1021);
return false;
}
this._innerAction = action;
this._speed = speed;
return true;
},
clone:function () {
var action = new cc.Speed();
action.initWithAction(this._innerAction.clone(), this._speed);
return action;
},
startWithTarget:function (target) {
cc.Action.prototype.startWithTarget.call(this, target);
this._innerAction.startWithTarget(target);
},
stop:function () {
this._innerAction.stop();
cc.Action.prototype.stop.call(this);
},
step:function (dt) {
this._innerAction.step(dt * this._speed);
},
isDone:function () {
return this._innerAction.isDone();
},
reverse:function () {
return new cc.Speed(this._innerAction.reverse(), this._speed);
},
/*
* Set inner Action.
* @method setInnerAction
* @param {ActionInterval} action
*/
setInnerAction:function (action) {
if (this._innerAction !== action) {
this._innerAction = action;
}
},
/*
* Get inner Action.
* @method getInnerAction
* @return {ActionInterval}
*/
getInnerAction:function () {
return this._innerAction;
}
});
/**
* @module cc
*/
/**
* !#en
* Creates the speed action which changes the speed of an action, making it take longer (speed > 1)
* or less (speed < 1) time. <br/>
* Useful to simulate 'slow motion' or 'fast forward' effect.
* !#zh 修改目标动作的速率。
* @warning This action can't be Sequenceable because it is not an cc.IntervalAction
*
* @method speed
* @param {ActionInterval} action
* @param {Number} speed
* @return {Action}
* @example
* // change the target action speed;
* var action = cc.scaleTo(0.2, 1, 0.6);
* var newAction = cc.speed(action, 0.5);
*/
cc.speed = function (action, speed) {
return new cc.Speed(action, speed);
};
/*
* cc.Follow is a follow action which makes its target follows another node.
*
* @example
* //example
* //Instead of using cc.Camera as a "follower", use this action instead.
* layer.runAction(cc.follow(hero));
*
* @property {Number} leftBoundary - world leftBoundary.
* @property {Number} rightBoundary - world rightBoundary.
* @property {Number} topBoundary - world topBoundary.
* @property {Number} bottomBoundary - world bottomBoundary.
*
* @param {cc.Node} followedNode
* @param {Rect} rect
* @example
* // creates the action with a set boundary
* var followAction = new cc.Follow(node, cc.rect(0, 0, s.width * 2 - 100, s.height));
* this.runAction(followAction);
*
* // creates the action with no boundary set
* var followAction = new cc.Follow(node);
* this.runAction(followAction);
*
* @class
* @extends Action
*/
cc.Follow = cc.Class({
name: 'cc.Follow',
extends: cc.Action,
/*
* Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
* creates the action with a set boundary. <br/>
* creates the action with no boundary set.
* @param {cc.Node} followedNode
* @param {Rect} rect
*/
ctor:function (followedNode, rect) {
// node to follow
this._followedNode = null;
// whether camera should be limited to certain area
this._boundarySet = false;
// if screen size is bigger than the boundary - update not needed
this._boundaryFullyCovered = false;
// fast access to the screen dimensions
this._halfScreenSize = null;
this._fullScreenSize = null;
this.leftBoundary = 0.0;
this.rightBoundary = 0.0;
this.topBoundary = 0.0;
this.bottomBoundary = 0.0;
this._worldRect = cc.rect(0, 0, 0, 0);
if(followedNode)
rect ? this.initWithTarget(followedNode, rect)
: this.initWithTarget(followedNode);
},
clone:function () {
var action = new cc.Follow();
var locRect = this._worldRect;
var rect = new cc.Rect(locRect.x, locRect.y, locRect.width, locRect.height);
action.initWithTarget(this._followedNode, rect);
return action;
},
/*
* Get whether camera should be limited to certain area.
*
* @return {Boolean}
*/
isBoundarySet:function () {
return this._boundarySet;
},
/*
* alter behavior - turn on/off boundary.
*
* @param {Boolean} value
*/
setBoundarySet:function (value) {
this._boundarySet = value;
},
/*
* alter behavior - turn on/off boundary.
*
* @param {Boolean} value
* @deprecated since v2.4.6 , cc.Follow.setBoudarySet is deprecated, please use cc.Follow.setBoundarySet.
*/
setBoudarySet:function (value) {
this.setBoundarySet(value);
},
/*
* initializes the action with a set boundary.
*
* @param {cc.Node} followedNode
* @param {Rect} [rect=]
* @return {Boolean}
*/
initWithTarget:function (followedNode, rect) {
if (!followedNode) {
cc.errorID(1022);
return false;
}
var _this = this;
rect = rect || cc.rect(0, 0, 0, 0);
_this._followedNode = followedNode;
_this._worldRect = rect;
_this._boundarySet = !(rect.width === 0 && rect.height === 0);
_this._boundaryFullyCovered = false;
var winSize = cc.winSize;
_this._fullScreenSize = cc.v2(winSize.width, winSize.height);
_this._halfScreenSize = _this._fullScreenSize.mul(0.5);
if (_this._boundarySet) {
_this.leftBoundary = -((rect.x + rect.width) - _this._fullScreenSize.x);
_this.rightBoundary = -rect.x;
_this.topBoundary = -rect.y;
_this.bottomBoundary = -((rect.y + rect.height) - _this._fullScreenSize.y);
if (_this.rightBoundary < _this.leftBoundary) {
// screen width is larger than world's boundary width
//set both in the middle of the world
_this.rightBoundary = _this.leftBoundary = (_this.leftBoundary + _this.rightBoundary) / 2;
}
if (_this.topBoundary < _this.bottomBoundary) {
// screen width is larger than world's boundary width
//set both in the middle of the world
_this.topBoundary = _this.bottomBoundary = (_this.topBoundary + _this.bottomBoundary) / 2;
}
if ((_this.topBoundary === _this.bottomBoundary) && (_this.leftBoundary === _this.rightBoundary))
_this._boundaryFullyCovered = true;
}
return true;
},
step:function (dt) {
var targetWorldPos = this.target.convertToWorldSpaceAR(cc.Vec2.ZERO);
var followedWorldPos = this._followedNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
// compute the offset between followed and target node
var delta = targetWorldPos.sub(followedWorldPos);
var tempPos = this.target.parent.convertToNodeSpaceAR(delta.add(this._halfScreenSize));
if (this._boundarySet) {
// whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
if (this._boundaryFullyCovered)
return;
this.target.setPosition(misc.clampf(tempPos.x, this.leftBoundary, this.rightBoundary), misc.clampf(tempPos.y, this.bottomBoundary, this.topBoundary));
} else {
this.target.setPosition(tempPos.x, tempPos.y);
}
},
isDone:function () {
return ( !this._followedNode.activeInHierarchy );
},
stop:function () {
this.target = null;
cc.Action.prototype.stop.call(this);
}
});
/**
* !#en Create a follow action which makes its target follows another node.
* !#zh 追踪目标节点的位置。
* @method follow
* @param {Node} followedNode
* @param {Rect} rect
* @return {Action|Null} returns the cc.Follow object on success
* @example
* // example
* // creates the action with a set boundary
* var followAction = cc.follow(targetNode, cc.rect(0, 0, screenWidth * 2 - 100, screenHeight));
* node.runAction(followAction);
*
* // creates the action with no boundary set
* var followAction = cc.follow(targetNode);
* node.runAction(followAction);
*/
cc.follow = function (followedNode, rect) {
return new cc.Follow(followedNode, rect);
};

View File

@@ -0,0 +1,446 @@
/****************************************************************************
Copyright (c) 2008 Radu Gruian
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011 Vit Valentin
Copyright (c) 2011-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Orignal code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So
Adapted to cocos2d-x by Vit Valentin
Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada
****************************************************************************/
/**
* @module cc
*/
/*
* Returns the Cardinal Spline position for a given set of control points, tension and time. <br />
* CatmullRom Spline formula. <br />
* s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4
*
* @method cardinalSplineAt
* @param {Vec2} p0
* @param {Vec2} p1
* @param {Vec2} p2
* @param {Vec2} p3
* @param {Number} tension
* @param {Number} t
* @return {Vec2}
*/
function cardinalSplineAt (p0, p1, p2, p3, tension, t) {
var t2 = t * t;
var t3 = t2 * t;
/*
* Formula: s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4
*/
var s = (1 - tension) / 2;
var b1 = s * ((-t3 + (2 * t2)) - t); // s(-t3 + 2 t2 - t)P1
var b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1); // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
var b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2); // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
var b4 = s * (t3 - t2); // s(t3 - t2)P4
var x = (p0.x * b1 + p1.x * b2 + p2.x * b3 + p3.x * b4);
var y = (p0.y * b1 + p1.y * b2 + p2.y * b3 + p3.y * b4);
return cc.v2(x, y);
};
/*
* returns a point from the array
* @method getControlPointAt
* @param {Array} controlPoints
* @param {Number} pos
* @return {Array}
*/
function getControlPointAt (controlPoints, pos) {
var p = Math.min(controlPoints.length - 1, Math.max(pos, 0));
return controlPoints[p];
};
function reverseControlPoints (controlPoints) {
var newArray = [];
for (var i = controlPoints.length - 1; i >= 0; i--) {
newArray.push(cc.v2(controlPoints[i].x, controlPoints[i].y));
}
return newArray;
}
function cloneControlPoints (controlPoints) {
var newArray = [];
for (var i = 0; i < controlPoints.length; i++)
newArray.push(cc.v2(controlPoints[i].x, controlPoints[i].y));
return newArray;
}
/*
* Cardinal Spline path. http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
* Absolute coordinates.
*
* @class CardinalSplineTo
* @extends ActionInterval
*
* @param {Number} duration
* @param {Array} points array of control points
* @param {Number} tension
*
* @example
* //create a cc.CardinalSplineTo
* var action1 = cc.cardinalSplineTo(3, array, 0);
*/
cc.CardinalSplineTo = cc.Class({
name: 'cc.CardinalSplineTo',
extends: cc.ActionInterval,
ctor: function (duration, points, tension) {
/* Array of control points */
this._points = [];
this._deltaT = 0;
this._tension = 0;
this._previousPosition = null;
this._accumulatedDiff = null;
tension !== undefined && cc.CardinalSplineTo.prototype.initWithDuration.call(this, duration, points, tension);
},
initWithDuration:function (duration, points, tension) {
if (!points || points.length === 0) {
cc.errorID(1024);
return false;
}
if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) {
this.setPoints(points);
this._tension = tension;
return true;
}
return false;
},
clone:function () {
var action = new cc.CardinalSplineTo();
action.initWithDuration(this._duration, cloneControlPoints(this._points), this._tension);
return action;
},
startWithTarget:function (target) {
cc.ActionInterval.prototype.startWithTarget.call(this, target);
// Issue #1441 from cocos2d-iphone
this._deltaT = 1 / (this._points.length - 1);
this._previousPosition = cc.v2(this.target.x, this.target.y);
this._accumulatedDiff = cc.v2(0, 0);
},
update:function (dt) {
dt = this._computeEaseTime(dt);
var p, lt;
var ps = this._points;
// eg.
// p..p..p..p..p..p..p
// 1..2..3..4..5..6..7
// want p to be 1, 2, 3, 4, 5, 6
if (dt === 1) {
p = ps.length - 1;
lt = 1;
} else {
var locDT = this._deltaT;
p = 0 | (dt / locDT);
lt = (dt - locDT * p) / locDT;
}
var newPos = cardinalSplineAt(
getControlPointAt(ps, p - 1),
getControlPointAt(ps, p - 0),
getControlPointAt(ps, p + 1),
getControlPointAt(ps, p + 2),
this._tension, lt);
if (cc.macro.ENABLE_STACKABLE_ACTIONS) {
var tempX, tempY;
tempX = this.target.x - this._previousPosition.x;
tempY = this.target.y - this._previousPosition.y;
if (tempX !== 0 || tempY !== 0) {
var locAccDiff = this._accumulatedDiff;
tempX = locAccDiff.x + tempX;
tempY = locAccDiff.y + tempY;
locAccDiff.x = tempX;
locAccDiff.y = tempY;
newPos.x += tempX;
newPos.y += tempY;
}
}
this.updatePosition(newPos);
},
reverse:function () {
var reversePoints = reverseControlPoints(this._points);
return cc.cardinalSplineTo(this._duration, reversePoints, this._tension);
},
/*
* update position of target
* @method updatePosition
* @param {Vec2} newPos
*/
updatePosition:function (newPos) {
this.target.setPosition(newPos);
this._previousPosition = newPos;
},
/*
* Points getter
* @method getPoints
* @return {Array}
*/
getPoints:function () {
return this._points;
},
/**
* Points setter
* @method setPoints
* @param {Array} points
*/
setPoints:function (points) {
this._points = points;
}
});
/**
* !#en Creates an action with a Cardinal Spline array of points and tension.
* !#zh 按基数样条曲线轨迹移动到目标位置。
* @method cardinalSplineTo
* @param {Number} duration
* @param {Array} points array of control points
* @param {Number} tension
* @return {ActionInterval}
*
* @example
* //create a cc.CardinalSplineTo
* var action1 = cc.cardinalSplineTo(3, array, 0);
*/
cc.cardinalSplineTo = function (duration, points, tension) {
return new cc.CardinalSplineTo(duration, points, tension);
};
/*
* Cardinal Spline path. http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
* Relative coordinates.
*
* @class CardinalSplineBy
* @extends CardinalSplineTo
*
* @param {Number} duration
* @param {Array} points
* @param {Number} tension
*
* @example
* //create a cc.CardinalSplineBy
* var action1 = cc.cardinalSplineBy(3, array, 0);
*/
cc.CardinalSplineBy = cc.Class({
name: 'cc.CardinalSplineBy',
extends: cc.CardinalSplineTo,
ctor:function (duration, points, tension) {
this._startPosition = cc.v2(0, 0);
tension !== undefined && this.initWithDuration(duration, points, tension);
},
startWithTarget:function (target) {
cc.CardinalSplineTo.prototype.startWithTarget.call(this, target);
this._startPosition.x = target.x;
this._startPosition.y = target.y;
},
reverse:function () {
var copyConfig = this._points.slice();
var current;
//
// convert "absolutes" to "diffs"
//
var p = copyConfig[0];
for (var i = 1; i < copyConfig.length; ++i) {
current = copyConfig[i];
copyConfig[i] = current.sub(p);
p = current;
}
// convert to "diffs" to "reverse absolute"
var reverseArray = reverseControlPoints(copyConfig);
// 1st element (which should be 0,0) should be here too
p = reverseArray[ reverseArray.length - 1 ];
reverseArray.pop();
p.x = -p.x;
p.y = -p.y;
reverseArray.unshift(p);
for (var i = 1; i < reverseArray.length; ++i) {
current = reverseArray[i];
current.x = -current.x;
current.y = -current.y;
current.x += p.x;
current.y += p.y;
reverseArray[i] = current;
p = current;
}
return cc.cardinalSplineBy(this._duration, reverseArray, this._tension);
},
/**
* update position of target
* @method updatePosition
* @param {Vec2} newPos
*/
updatePosition:function (newPos) {
var pos = this._startPosition;
var posX = newPos.x + pos.x;
var posY = newPos.y + pos.y;
this._previousPosition.x = posX;
this._previousPosition.y = posY;
this.target.setPosition(posX, posY);
},
clone:function () {
var a = new cc.CardinalSplineBy();
a.initWithDuration(this._duration, cloneControlPoints(this._points), this._tension);
return a;
}
});
/**
* !#en Creates an action with a Cardinal Spline array of points and tension.
* !#zh 按基数样条曲线轨迹移动指定的距离。
* @method cardinalSplineBy
* @param {Number} duration
* @param {Array} points
* @param {Number} tension
*
* @return {ActionInterval}
*/
cc.cardinalSplineBy = function (duration, points, tension) {
return new cc.CardinalSplineBy(duration, points, tension);
};
/*
* An action that moves the target with a CatmullRom curve to a destination point.<br/>
* A Catmull Rom is a Cardinal Spline with a tension of 0.5. <br/>
* http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
* Absolute coordinates.
*
* @class CatmullRomTo
* @extends CardinalSplineTo
*
* @param {Number} dt
* @param {Array} points
*
* @example
* var action1 = cc.catmullRomTo(3, array);
*/
cc.CatmullRomTo = cc.Class({
name: 'cc.CatmullRomTo',
extends: cc.CardinalSplineTo,
ctor: function(dt, points) {
points && this.initWithDuration(dt, points);
},
initWithDuration:function (dt, points) {
return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
},
clone:function () {
var action = new cc.CatmullRomTo();
action.initWithDuration(this._duration, cloneControlPoints(this._points));
return action;
}
});
/**
* !#en Creates an action with a Cardinal Spline array of points and tension.
* !#zh 按 Catmull Rom 样条曲线轨迹移动到目标位置。
* @method catmullRomTo
* @param {Number} dt
* @param {Array} points
* @return {ActionInterval}
*
* @example
* var action1 = cc.catmullRomTo(3, array);
*/
cc.catmullRomTo = function (dt, points) {
return new cc.CatmullRomTo(dt, points);
};
/*
* An action that moves the target with a CatmullRom curve by a certain distance. <br/>
* A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/>
* http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
* Relative coordinates.
*
* @class CatmullRomBy
* @extends CardinalSplineBy
*
* @param {Number} dt
* @param {Array} points
*
* @example
* var action1 = cc.catmullRomBy(3, array);
*/
cc.CatmullRomBy = cc.Class({
name: 'cc.CatmullRomBy',
extends: cc.CardinalSplineBy,
ctor: function(dt, points) {
points && this.initWithDuration(dt, points);
},
initWithDuration:function (dt, points) {
return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
},
clone:function () {
var action = new cc.CatmullRomBy();
action.initWithDuration(this._duration, cloneControlPoints(this._points));
return action;
}
});
/**
* !#en Creates an action with a Cardinal Spline array of points and tension.
* !#zh 按 Catmull Rom 样条曲线轨迹移动指定的距离。
* @method catmullRomBy
* @param {Number} dt
* @param {Array} points
* @return {ActionInterval}
* @example
* var action1 = cc.catmullRomBy(3, array);
*/
cc.catmullRomBy = function (dt, points) {
return new cc.CatmullRomBy(dt, points);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,569 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc
*/
/**
* !#en Instant actions are immediate actions. They don't have a duration like the ActionInterval actions.
* !#zh 即时动作,这种动作立即就会执行,继承自 FiniteTimeAction。
* @class ActionInstant
* @extends FiniteTimeAction
*/
cc.ActionInstant = cc.Class({
name: 'cc.ActionInstant',
extends: cc.FiniteTimeAction,
isDone:function () {
return true;
},
step:function (dt) {
this.update(1);
},
update:function (dt) {
//nothing
},
/**
* returns a reversed action. <br />
* For example: <br />
* - The action is x coordinates of 0 move to 100. <br />
* - The reversed action will be x of 100 move to 0.
* @returns {Action}
*/
reverse:function(){
return this.clone();
},
clone:function(){
return new cc.ActionInstant();
}
});
/**
* @module cc
*/
/*
* Show the node.
* @class Show
* @extends ActionInstant
*/
cc.Show = cc.Class({
name: 'cc.Show',
extends: cc.ActionInstant,
update:function (dt) {
var _renderComps = this.target.getComponentsInChildren(cc.RenderComponent);
for (var i = 0; i < _renderComps.length; ++i) {
var render = _renderComps[i];
render.enabled = true;
}
},
reverse:function () {
return new cc.Hide();
},
clone:function(){
return new cc.Show();
}
});
/**
* !#en Show the Node.
* !#zh 立即显示。
* @method show
* @return {ActionInstant}
* @example
* // example
* var showAction = cc.show();
*/
cc.show = function () {
return new cc.Show();
};
/*
* Hide the node.
* @class Hide
* @extends ActionInstant
*/
cc.Hide = cc.Class({
name: 'cc.Hide',
extends: cc.ActionInstant,
update:function (dt) {
var _renderComps = this.target.getComponentsInChildren(cc.RenderComponent);
for (var i = 0; i < _renderComps.length; ++i) {
var render = _renderComps[i];
render.enabled = false;
}
},
reverse:function () {
return new cc.Show();
},
clone:function(){
return new cc.Hide();
}
});
/**
* !#en Hide the node.
* !#zh 立即隐藏。
* @method hide
* @return {ActionInstant}
* @example
* // example
* var hideAction = cc.hide();
*/
cc.hide = function () {
return new cc.Hide();
};
/*
* Toggles the visibility of a node.
* @class ToggleVisibility
* @extends ActionInstant
*/
cc.ToggleVisibility = cc.Class({
name: 'cc.ToggleVisibility',
extends: cc.ActionInstant,
update:function (dt) {
var _renderComps = this.target.getComponentsInChildren(cc.RenderComponent);
for (var i = 0; i < _renderComps.length; ++i) {
var render = _renderComps[i];
render.enabled = !render.enabled;
}
},
reverse:function () {
return new cc.ToggleVisibility();
},
clone:function(){
return new cc.ToggleVisibility();
}
});
/**
* !#en Toggles the visibility of a node.
* !#zh 显隐状态切换。
* @method toggleVisibility
* @return {ActionInstant}
* @example
* // example
* var toggleVisibilityAction = cc.toggleVisibility();
*/
cc.toggleVisibility = function () {
return new cc.ToggleVisibility();
};
/*
* Delete self in the next frame.
* @class RemoveSelf
* @extends ActionInstant
* @param {Boolean} [isNeedCleanUp=true]
*
* @example
* // example
* var removeSelfAction = new cc.RemoveSelf(false);
*/
cc.RemoveSelf = cc.Class({
name: 'cc.RemoveSelf',
extends: cc.ActionInstant,
ctor:function(isNeedCleanUp){
this._isNeedCleanUp = true;
isNeedCleanUp !== undefined && this.init(isNeedCleanUp);
},
update:function(dt){
this.target.removeFromParent(this._isNeedCleanUp);
},
init:function(isNeedCleanUp){
this._isNeedCleanUp = isNeedCleanUp;
return true;
},
reverse:function(){
return new cc.RemoveSelf(this._isNeedCleanUp);
},
clone:function(){
return new cc.RemoveSelf(this._isNeedCleanUp);
}
});
/**
* !#en Create a RemoveSelf object with a flag indicate whether the target should be cleaned up while removing.
* !#zh 从父节点移除自身。
* @method removeSelf
* @param {Boolean} [isNeedCleanUp = true]
* @return {ActionInstant}
*
* @example
* // example
* var removeSelfAction = cc.removeSelf();
*/
cc.removeSelf = function(isNeedCleanUp){
return new cc.RemoveSelf(isNeedCleanUp);
};
/*
* Create an action to destroy self.
* @class DestroySelf
* @extends ActionInstant
*
* @example
* var destroySelfAction = new cc.DestroySelf();
*/
cc.DestroySelf = cc.Class({
name: 'cc.DestroySelf',
extends: cc.ActionInstant,
update () {
this.target.destroy();
},
reverse () {
return new cc.DestroySelf();
},
clone () {
return new cc.DestroySelf();
}
});
/**
* !#en Destroy self
* !#zh 创建一个销毁自身的动作。
* @method destroySelf
* @return {ActionInstant}
*
* @example
* var destroySelfAction = cc.destroySelf();
*/
cc.destroySelf = function () {
return new cc.DestroySelf();
};
/*
* Flips the sprite horizontally.
* @class FlipX
* @extends ActionInstant
* @param {Boolean} flip Indicate whether the target should be flipped or not
*
* @example
* var flipXAction = new cc.FlipX(true);
*/
cc.FlipX = cc.Class({
name: 'cc.FlipX',
extends: cc.ActionInstant,
ctor:function(flip){
this._flippedX = false;
flip !== undefined && this.initWithFlipX(flip);
},
/*
* initializes the action with a set flipX.
* @param {Boolean} flip
* @return {Boolean}
*/
initWithFlipX:function (flip) {
this._flippedX = flip;
return true;
},
update:function (dt) {
this.target.scaleX = Math.abs(this.target.scaleX) * (this._flippedX ? -1 : 1);
},
reverse:function () {
return new cc.FlipX(!this._flippedX);
},
clone:function(){
var action = new cc.FlipX();
action.initWithFlipX(this._flippedX);
return action;
}
});
/**
* !#en Create a FlipX action to flip or unflip the target.
* !#zh X轴翻转。
* @method flipX
* @param {Boolean} flip Indicate whether the target should be flipped or not
* @return {ActionInstant}
* @example
* var flipXAction = cc.flipX(true);
*/
cc.flipX = function (flip) {
return new cc.FlipX(flip);
};
/*
* Flips the sprite vertically
* @class FlipY
* @extends ActionInstant
* @param {Boolean} flip
* @example
* var flipYAction = new cc.FlipY(true);
*/
cc.FlipY = cc.Class({
name: 'cc.FlipY',
extends: cc.ActionInstant,
ctor: function(flip){
this._flippedY = false;
flip !== undefined && this.initWithFlipY(flip);
},
/*
* initializes the action with a set flipY.
* @param {Boolean} flip
* @return {Boolean}
*/
initWithFlipY:function (flip) {
this._flippedY = flip;
return true;
},
update:function (dt) {
this.target.scaleY = Math.abs(this.target.scaleY) * (this._flippedY ? -1 : 1);
},
reverse:function () {
return new cc.FlipY(!this._flippedY);
},
clone:function(){
var action = new cc.FlipY();
action.initWithFlipY(this._flippedY);
return action;
}
});
/**
* !#en Create a FlipY action to flip or unflip the target.
* !#zh Y轴翻转。
* @method flipY
* @param {Boolean} flip
* @return {ActionInstant}
* @example
* var flipYAction = cc.flipY(true);
*/
cc.flipY = function (flip) {
return new cc.FlipY(flip);
};
/*
* Places the node in a certain position
* @class Place
* @extends ActionInstant
* @param {Vec2|Number} pos
* @param {Number} [y]
* @example
* var placeAction = new cc.Place(cc.v2(200, 200));
* var placeAction = new cc.Place(200, 200);
*/
cc.Place = cc.Class({
name: 'cc.Place',
extends: cc.ActionInstant,
ctor:function(pos, y){
this._x = 0;
this._y = 0;
if (pos !== undefined) {
if (pos.x !== undefined) {
y = pos.y;
pos = pos.x;
}
this.initWithPosition(pos, y);
}
},
/*
* Initializes a Place action with a position
* @param {number} x
* @param {number} y
* @return {Boolean}
*/
initWithPosition: function (x, y) {
this._x = x;
this._y = y;
return true;
},
update:function (dt) {
this.target.setPosition(this._x, this._y);
},
clone:function(){
var action = new cc.Place();
action.initWithPosition(this._x, this._y);
return action;
}
});
/**
* !#en Creates a Place action with a position.
* !#zh 放置在目标位置。
* @method place
* @param {Vec2|Number} pos
* @param {Number} [y]
* @return {ActionInstant}
* @example
* // example
* var placeAction = cc.place(cc.v2(200, 200));
* var placeAction = cc.place(200, 200);
*/
cc.place = function (pos, y) {
return new cc.Place(pos, y);
};
/*
* Calls a 'callback'.
* @class CallFunc
* @extends ActionInstant
* @param {function} selector
* @param {object} [selectorTarget=null]
* @param {*} [data=null] data for function, it accepts all data types.
* @example
* // example
* // CallFunc without data
* var finish = new cc.CallFunc(this.removeSprite, this);
*
* // CallFunc with data
* var finish = new cc.CallFunc(this.removeFromParentAndCleanup, this, true);
*/
cc.CallFunc = cc.Class({
name: 'cc.CallFunc',
extends: cc.ActionInstant,
/*
* Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
* Creates a CallFunc action with the callback.
* @param {function} selector
* @param {object} [selectorTarget=null]
* @param {*} [data=null] data for function, it accepts all data types.
*/
ctor:function(selector, selectorTarget, data){
this._selectorTarget = null;
this._function = null;
this._data = null;
this.initWithFunction(selector, selectorTarget, data);
},
/*
* Initializes the action with a function or function and its target
* @param {function} selector
* @param {object|Null} selectorTarget
* @param {*|Null} [data] data for function, it accepts all data types.
* @return {Boolean}
*/
initWithFunction:function (selector, selectorTarget, data) {
if (selector) {
this._function = selector;
}
if (selectorTarget) {
this._selectorTarget = selectorTarget;
}
if (data !== undefined) {
this._data = data;
}
return true;
},
/*
* execute the function.
*/
execute:function () {
if (this._function) {
this._function.call(this._selectorTarget, this.target, this._data);
}
},
update:function (dt) {
this.execute();
},
/*
* Get selectorTarget.
* @return {object}
*/
getTargetCallback:function () {
return this._selectorTarget;
},
/*
* Set selectorTarget.
* @param {object} sel
*/
setTargetCallback:function (sel) {
if (sel !== this._selectorTarget) {
if (this._selectorTarget)
this._selectorTarget = null;
this._selectorTarget = sel;
}
},
clone:function(){
var action = new cc.CallFunc();
action.initWithFunction(this._function, this._selectorTarget, this._data);
return action;
}
});
/**
* !#en Creates the action with the callback.
* !#zh 执行回调函数。
* @method callFunc
* @param {function} selector
* @param {object} [selectorTarget=null]
* @param {*} [data=null] - data for function, it accepts all data types.
* @return {ActionInstant}
* @example
* // example
* // CallFunc without data
* var finish = cc.callFunc(this.removeSprite, this);
*
* // CallFunc with data
* var finish = cc.callFunc(this.removeFromParentAndCleanup, this._grossini, true);
*/
cc.callFunc = function (selector, selectorTarget, data) {
return new cc.CallFunc(selector, selectorTarget, data);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,509 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
require('../core/platform/CCClass');
var js = require('../core/platform/js');
/*
* @class HashElement
* @constructor
* @private
*/
var HashElement = function () {
this.actions = [];
this.target = null; //ccobject
this.actionIndex = 0;
this.currentAction = null; //CCAction
this.paused = false;
this.lock = false;
};
/**
* !#en
* cc.ActionManager is a class that can manage actions.<br/>
* Normally you won't need to use this class directly. 99% of the cases you will use the CCNode interface,
* which uses this class's singleton object.
* But there are some cases where you might need to use this class. <br/>
* Examples:<br/>
* - When you want to run an action where the target is different from a CCNode.<br/>
* - When you want to pause / resume the actions<br/>
* !#zh
* cc.ActionManager 是可以管理动作的单例类。<br/>
* 通常你并不需要直接使用这个类99%的情况您将使用 CCNode 的接口。<br/>
* 但也有一些情况下,您可能需要使用这个类。 <br/>
* 例如:
* - 当你想要运行一个动作,但目标不是 CCNode 类型时。 <br/>
* - 当你想要暂停/恢复动作时。 <br/>
* @class ActionManager
* @example {@link cocos2d/core/CCActionManager/ActionManager.js}
*/
cc.ActionManager = function () {
this._hashTargets = js.createMap(true);
this._arrayTargets = [];
this._currentTarget = null;
cc.director._scheduler && cc.director._scheduler.enableForTarget(this);
};
cc.ActionManager.prototype = {
constructor: cc.ActionManager,
_elementPool: [],
_searchElementByTarget: function (arr, target) {
for (var k = 0; k < arr.length; k++) {
if (target === arr[k].target)
return arr[k];
}
return null;
},
_getElement: function (target, paused) {
var element = this._elementPool.pop();
if (!element) {
element = new HashElement();
}
element.target = target;
element.paused = !!paused;
return element;
},
_putElement: function (element) {
element.actions.length = 0;
element.actionIndex = 0;
element.currentAction = null;
element.paused = false;
element.target = null;
element.lock = false;
this._elementPool.push(element);
},
/**
* !#en
* Adds an action with a target.<br/>
* If the target is already present, then the action will be added to the existing target.
* If the target is not present, a new instance of this target will be created either paused or not, and the action will be added to the newly created target.
* When the target is paused, the queued actions won't be 'ticked'.
* !#zh
* 增加一个动作,同时还需要提供动作的目标对象,目标对象是否暂停作为参数。<br/>
* 如果目标已存在,动作将会被直接添加到现有的节点中。<br/>
* 如果目标不存在,将为这一目标创建一个新的实例,并将动作添加进去。<br/>
* 当目标状态的 paused 为 true动作将不会被执行
*
* @method addAction
* @param {Action} action
* @param {Node} target
* @param {Boolean} paused
*/
addAction: function (action, target, paused) {
if (!action || !target) {
cc.errorID(1000);
return;
}
//check if the action target already exists
var element = this._hashTargets[target._id];
//if doesn't exists, create a hashelement and push in mpTargets
if (!element) {
element = this._getElement(target, paused);
this._hashTargets[target._id] = element;
this._arrayTargets.push(element);
}
else if (!element.actions) {
element.actions = [];
}
element.actions.push(action);
action.startWithTarget(target);
},
/**
* !#en Removes all actions from all the targets.
* !#zh 移除所有对象的所有动作。
* @method removeAllActions
*/
removeAllActions: function () {
var locTargets = this._arrayTargets;
for (var i = 0; i < locTargets.length; i++) {
var element = locTargets[i];
if (element)
this._putElement(element);
}
this._arrayTargets.length = 0;
this._hashTargets = js.createMap(true);
},
/**
* !#en
* Removes all actions from a certain target. <br/>
* All the actions that belongs to the target will be removed.
* !#zh
* 移除指定对象上的所有动作。<br/>
* 属于该目标的所有的动作将被删除。
* @method removeAllActionsFromTarget
* @param {Node} target
* @param {Boolean} forceDelete
*/
removeAllActionsFromTarget: function (target, forceDelete) {
// explicit null handling
if (target == null)
return;
var element = this._hashTargets[target._id];
if (element) {
element.actions.length = 0;
this._deleteHashElement(element);
}
},
/**
* !#en Removes an action given an action reference.
* !#zh 移除指定的动作。
* @method removeAction
* @param {Action} action
*/
removeAction: function (action) {
// explicit null handling
if (!action) {
return;
}
var target = action.getOriginalTarget();
var element = this._hashTargets[target._id];
if (!element) {
return;
}
for (var i = 0; i < element.actions.length; i++) {
if (element.actions[i] === action) {
element.actions.splice(i, 1);
// update actionIndex in case we are in tick. looping over the actions
if (element.actionIndex >= i)
element.actionIndex--;
break;
}
}
},
/**
* @internal
*/
_removeActionByTag (tag, element, target) {
for (var i = 0, l = element.actions.length; i < l; ++i) {
var action = element.actions[i];
if (action && action.getTag() === tag) {
if (target && action.getOriginalTarget() !== target) {
continue;
}
this._removeActionAtIndex(i, element);
break;
}
}
},
/**
* @internal
*/
_removeAllActionsByTag (tag, element, target) {
for (var i = element.actions.length - 1; i >= 0; --i) {
var action = element.actions[i];
if (action && action.getTag() === tag) {
if (target && action.getOriginalTarget() !== target) {
continue;
}
this._removeActionAtIndex(i, element);
}
}
},
/**
* !#en Removes an action given its tag and the target.
* !#zh 删除指定对象下特定标签的一个动作,将删除首个匹配到的动作。
* @method removeActionByTag
* @param {Number} tag
* @param {Node} [target]
*/
removeActionByTag: function (tag, target) {
if(tag === cc.Action.TAG_INVALID)
cc.logID(1002);
let hashTargets = this._hashTargets;
if (target) {
var element = hashTargets[target._id];
if (element) {
this._removeActionByTag(tag, element, target);
}
}
else {
for (let name in hashTargets) {
let element = hashTargets[name];
this._removeActionByTag(tag, element);
}
}
},
/**
* !#en Removes all actions given the tag and the target.
* !#zh 删除指定对象下特定标签的所有动作。
* @method removeAllActionsByTag
* @param {Number} tag
* @param {Node} [target]
*/
removeAllActionsByTag: function (tag, target) {
if (tag === cc.Action.TAG_INVALID)
cc.logID(1002);
let hashTargets = this._hashTargets;
if (target) {
var element = hashTargets[target._id];
if (element) {
this._removeAllActionsByTag(tag, element, target);
}
}
else {
for (let name in hashTargets) {
let element = hashTargets[name];
this._removeAllActionsByTag(tag, element);
}
}
},
/**
* !#en Gets an action given its tag an a target.
* !#zh 通过目标对象和标签获取一个动作。
* @method getActionByTag
* @param {Number} tag
* @param {Node} target
* @return {Action|Null} return the Action with the given tag on success
*/
getActionByTag: function (tag, target) {
if(tag === cc.Action.TAG_INVALID)
cc.logID(1004);
var element = this._hashTargets[target._id];
if (element) {
if (element.actions != null) {
for (var i = 0; i < element.actions.length; ++i) {
var action = element.actions[i];
if (action && action.getTag() === tag)
return action;
}
}
cc.logID(1005, tag);
}
return null;
},
/**
* !#en
* Returns the numbers of actions that are running in a certain target. <br/>
* Composable actions are counted as 1 action. <br/>
* Example: <br/>
* - If you are running 1 Sequence of 7 actions, it will return 1. <br/>
* - If you are running 7 Sequences of 2 actions, it will return 7.
* !#zh
* 返回指定对象下所有正在运行的动作数量。 <br/>
* 组合动作被算作一个动作。<br/>
* 例如:<br/>
* - 如果您正在运行 7 个动作组成的序列动作Sequence这个函数将返回 1。<br/>
* - 如果你正在运行 2 个序列动作Sequence和 5 个普通动作,这个函数将返回 7。<br/>
*
* @method getNumberOfRunningActionsInTarget
* @param {Node} target
* @return {Number}
*/
getNumberOfRunningActionsInTarget: function (target) {
var element = this._hashTargets[target._id];
if (element)
return (element.actions) ? element.actions.length : 0;
return 0;
},
/**
* !#en Pauses the target: all running actions and newly added actions will be paused.
* !#zh 暂停指定对象:所有正在运行的动作和新添加的动作都将会暂停。
* @method pauseTarget
* @param {Node} target
*/
pauseTarget: function (target) {
var element = this._hashTargets[target._id];
if (element)
element.paused = true;
},
/**
* !#en Resumes the target. All queued actions will be resumed.
* !#zh 让指定目标恢复运行。在执行序列中所有被暂停的动作将重新恢复运行。
* @method resumeTarget
* @param {Node} target
*/
resumeTarget: function (target) {
var element = this._hashTargets[target._id];
if (element)
element.paused = false;
},
/**
* !#en Pauses all running actions, returning a list of targets whose actions were paused.
* !#zh 暂停所有正在运行的动作,返回一个包含了那些动作被暂停了的目标对象的列表。
* @method pauseAllRunningActions
* @return {Array} a list of targets whose actions were paused.
*/
pauseAllRunningActions: function () {
var idsWithActions = [];
var locTargets = this._arrayTargets;
for(var i = 0; i < locTargets.length; i++){
var element = locTargets[i];
if(element && !element.paused){
element.paused = true;
idsWithActions.push(element.target);
}
}
return idsWithActions;
},
/**
* !#en Resume a set of targets (convenience function to reverse a pauseAllRunningActions or pauseTargets call).
* !#zh 让一组指定对象恢复运行(用来逆转 pauseAllRunningActions 效果的便捷函数)。
* @method resumeTargets
* @param {Array} targetsToResume
*/
resumeTargets: function (targetsToResume) {
if (!targetsToResume)
return;
for (var i = 0; i < targetsToResume.length; i++) {
if(targetsToResume[i])
this.resumeTarget(targetsToResume[i]);
}
},
/**
* !#en Pause a set of targets.
* !#zh 暂停一组指定对象。
* @method pauseTargets
* @param {Array} targetsToPause
*/
pauseTargets: function (targetsToPause) {
if (!targetsToPause)
return;
for (var i = 0; i < targetsToPause.length; i++) {
if (targetsToPause[i])
this.pauseTarget(targetsToPause[i]);
}
},
/**
* !#en
* purges the shared action manager. It releases the retained instance. <br/>
* because it uses this, so it can not be static.
* !#zh
* 清除共用的动作管理器。它释放了持有的实例。 <br/>
* 因为它使用 this因此它不能是静态的。
* @method purgeSharedManager
*/
purgeSharedManager: function () {
cc.director.getScheduler().unscheduleUpdate(this);
},
//protected
_removeActionAtIndex: function (index, element) {
var action = element.actions[index];
element.actions.splice(index, 1);
// update actionIndex in case we are in tick. looping over the actions
if (element.actionIndex >= index)
element.actionIndex--;
if (element.actions.length === 0) {
this._deleteHashElement(element);
}
},
_deleteHashElement: function (element) {
var ret = false;
if (element && !element.lock) {
if (this._hashTargets[element.target._id]) {
delete this._hashTargets[element.target._id];
var targets = this._arrayTargets;
for (var i = 0, l = targets.length; i < l; i++) {
if (targets[i] === element) {
targets.splice(i, 1);
break;
}
}
this._putElement(element);
ret = true;
}
}
return ret;
},
/**
* !#en The ActionManager update。
* !#zh ActionManager 主循环。
* @method update
* @param {Number} dt delta time in seconds
*/
update: function (dt) {
var locTargets = this._arrayTargets , locCurrTarget;
for (var elt = 0; elt < locTargets.length; elt++) {
this._currentTarget = locTargets[elt];
locCurrTarget = this._currentTarget;
if (!locCurrTarget.paused && locCurrTarget.actions) {
locCurrTarget.lock = true;
// The 'actions' CCMutableArray may change while inside this loop.
for (locCurrTarget.actionIndex = 0; locCurrTarget.actionIndex < locCurrTarget.actions.length; locCurrTarget.actionIndex++) {
locCurrTarget.currentAction = locCurrTarget.actions[locCurrTarget.actionIndex];
if (!locCurrTarget.currentAction)
continue;
//use for speed
locCurrTarget.currentAction.step(dt * ( locCurrTarget.currentAction._speedMethod ? locCurrTarget.currentAction._speed : 1 ) );
if (locCurrTarget.currentAction && locCurrTarget.currentAction.isDone()) {
locCurrTarget.currentAction.stop();
var action = locCurrTarget.currentAction;
// Make currentAction nil to prevent removeAction from salvaging it.
locCurrTarget.currentAction = null;
this.removeAction(action);
}
locCurrTarget.currentAction = null;
}
locCurrTarget.lock = false;
}
// only delete currentTarget if no actions were scheduled during the cycle (issue #481)
if (locCurrTarget.actions.length === 0) {
this._deleteHashElement(locCurrTarget) && elt--;
}
}
}
};
if (CC_TEST) {
cc.ActionManager.prototype.isTargetPaused_TEST = function (target) {
var element = this._hashTargets[target._id];
return element.paused;
};
}

View File

@@ -0,0 +1,32 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
require('./CCActionManager');
require('./CCAction');
require('./CCActionInterval');
require('./CCActionInstant');
require('./CCActionEase');
require('./CCActionCatmullRom');
require('./tween');

View File

@@ -0,0 +1,706 @@
import { bezier } from '../animation/bezier';
let _tweenID = 0;
let TweenAction = cc.Class({
name: 'cc.TweenAction',
extends: cc.ActionInterval,
ctor (duration, props, opts) {
this._opts = opts = opts || Object.create(null);
this._props = Object.create(null);
// global easing or progress used for this action
opts.progress = opts.progress || this.progress;
if (opts.easing && typeof opts.easing === 'string') {
let easingName = opts.easing;
opts.easing = cc.easing[easingName];
!opts.easing && cc.warnID(1031, easingName);
}
let relative = this._opts.relative;
for (let name in props) {
let value = props[name];
// property may have custom easing or progress function
let easing, progress;
if (value.value !== undefined && (value.easing || value.progress)) {
if (typeof value.easing === 'string') {
easing = cc.easing[value.easing];
!easing && cc.warnID(1031, value.easing);
}
else {
easing = value.easing;
}
progress = value.progress;
value = value.value;
}
let isNumber = typeof value === 'number';
if (!isNumber && (!value.lerp || (relative && !value.add && !value.mul) || !value.clone)) {
cc.warn(`Can not animate ${name} property, because it do not have [lerp, (add|mul), clone] function.`);
continue;
}
let prop = Object.create(null);
prop.value = value;
prop.easing = easing;
prop.progress = progress;
this._props[name] = prop;
}
this._originProps = props;
this.initWithDuration(duration);
},
clone () {
var action = new TweenAction(this._duration, this._originProps, this._opts);
this._cloneDecoration(action);
return action;
},
startWithTarget (target) {
cc.ActionInterval.prototype.startWithTarget.call(this, target);
let relative = !!this._opts.relative;
let props = this._props;
for (let name in props) {
let value = target[name];
let prop = props[name];
if (typeof value === 'number') {
prop.start = value;
prop.current = value;
prop.end = relative ? value + prop.value : prop.value;
}
else {
prop.start = value.clone();
prop.current = value.clone();
prop.end = relative ? (value.add || value.mul).call(value, prop.value) : prop.value;
}
}
},
update (t) {
let opts = this._opts;
let easingTime = t;
if (opts.easing) easingTime = opts.easing(t);
let target = this.target;
if (!target) return;
let props = this._props;
let progress = opts.progress;
for (let name in props) {
let prop = props[name];
let time = prop.easing ? prop.easing(t) : easingTime;
let current = prop.current = (prop.progress || progress)(prop.start, prop.end, prop.current, time);
target[name] = current;
}
let onUpdate = opts.onUpdate;
if (onUpdate) {
onUpdate(target, t)
}
},
progress (start, end, current, t) {
if (typeof start === 'number') {
current = start + (end - start) * t;
}
else {
start.lerp(end, t, current);
}
return current;
}
});
let SetAction = cc.Class({
name: 'cc.SetAction',
extends: cc.ActionInstant,
ctor (props) {
this._props = {};
props !== undefined && this.init(props);
},
init (props) {
for (let name in props) {
this._props[name] = props[name];
}
return true;
},
update () {
let props = this._props;
let target = this.target;
for (let name in props) {
target[name] = props[name];
}
},
clone () {
var action = new SetAction();
action.init(this._props);
return action;
}
});
/**
* !#en
* Tween provide a simple and flexible way to create action. Tween's api is more flexible than `cc.Action`:
* - Support creating an action sequence in chained api.
* - Support animate any objects' any properties, not limited to node's properties. By contrast, `cc.Action` needs to create a new action class to support new node property.
* - Support working with `cc.Action`.
* - Support easing and progress function.
* !#zh
* Tween 提供了一个简单灵活的方法来创建 action。相对于 Cocos 传统的 `cc.Action``cc.Tween` 在创建动画上要灵活非常多:
* - 支持以链式结构的方式创建一个动画序列。
* - 支持对任意对象的任意属性进行缓动,不再局限于节点上的属性,而 `cc.Action` 添加一个属性的支持时还需要添加一个新的 action 类型。
* - 支持与 `cc.Action` 混用。
* - 支持设置 {{#crossLink "Easing"}}{{/crossLink}} 或者 progress 函数。
* @class Tween
* @example
* cc.tween(node)
* .to(1, {scale: 2, position: cc.v3(100, 100, 100)})
* .call(() => { console.log('This is a callback'); })
* .by(1, {scale: 3, position: cc.v3(200, 200, 200)}, {easing: 'sineOutIn'})
* .start(cc.find('Canvas/cocos'));
* @typescript Tween<T = any>
*/
function Tween (target) {
this._actions = [];
this._finalAction = null;
this._target = target;
this._tag = cc.Action.TAG_INVALID;
}
/**
* @method constructor
* @param {Object} [target]
*/
/**
* !#en Stop all tweens
* !#zh 停止所有缓动
* @method stopAll
* @static
*/
Tween.stopAll = function () {
cc.director.getActionManager().removeAllActions();
}
/**
* !#en Stop all tweens by tag
* !#zh 停止所有指定标签的缓动
* @method stopAllByTag
* @static
* @param {number} tag
*/
Tween.stopAllByTag = function (tag) {
cc.director.getActionManager().removeAllActionsByTag(tag);
}
/**
* !#en Stop all tweens by target
* !#zh 停止所有指定对象的缓动
* @method stopAllByTarget
* @static
* @param {Object} target
*/
Tween.stopAllByTarget = function (target) {
cc.director.getActionManager().removeAllActionsFromTarget(target);
}
/**
* !#en
* Insert an action or tween to this sequence
* !#zh
* 插入一个 action 或者 tween 到队列中
* @method then
* @param {Action|Tween} other
* @return {Tween}
* @typescript then(other: Action|Tween<T>): Tween<T>
*/
Tween.prototype.then = function (other) {
if (other instanceof cc.Action) {
this._actions.push(other.clone());
}
else {
this._actions.push(other._union());
}
return this;
};
/**
* !#en
* Set tween target
* !#zh
* 设置 tween 的 target
* @method target
* @param {Object} target
* @return {Tween}
* @typescript target(target: any): Tween<T>
*/
Tween.prototype.target = function (target) {
this._target = target;
return this;
};
/**
* !#en
* Start this tween
* !#zh
* 运行当前 tween
* @method start
* @return {Tween}
* @typescript start(): Tween<T>
*/
Tween.prototype.start = function () {
let target = this._target;
if (!target) {
cc.warn('Please set target to tween first');
return this;
}
if (target instanceof cc.Object && !target.isValid) {
return;
}
if (this._finalAction) {
cc.director.getActionManager().removeAction(this._finalAction);
}
this._finalAction = this._union();
if (target._id === undefined) {
target._id = ++_tweenID;
}
this._finalAction.setTag(this._tag);
cc.director.getActionManager().addAction(this._finalAction, target, false);
return this;
};
/**
* !#en
* Stop this tween
* !#zh
* 停止当前 tween
* @method stop
* @return {Tween}
* @typescript stop(): Tween<T>
*/
Tween.prototype.stop = function () {
if (this._finalAction) {
cc.director.getActionManager().removeAction(this._finalAction);
}
return this;
};
/**
* !#en Sets tween tag
* !#zh 设置缓动的标签
* @method tag
* @param {number} tag
* @return {Tween}
* @typescript tag(tag: number): Tween<T>
*/
Tween.prototype.tag = function (tag) {
this._tag = tag;
return this;
};
/**
* !#en
* Clone a tween
* !#zh
* 克隆当前 tween
* @method clone
* @param {Object} [target]
* @return {Tween}
* @typescript clone(target?: any): Tween<T>
*/
Tween.prototype.clone = function (target) {
let action = this._union();
return cc.tween(target).then(action.clone());
};
/**
* !#en
* Integrate all previous actions to an action.
* !#zh
* 将之前所有的 action 整合为一个 action。
* @method union
* @return {Tween}
* @typescritp union(): Tween<T>
*/
Tween.prototype.union = function () {
let action = this._union();
this._actions.length = 0;
this._actions.push(action);
return this;
};
Tween.prototype._union = function () {
let actions = this._actions;
if (actions.length === 1) {
actions = actions[0];
}
else {
actions = cc.sequence(actions);
}
return actions;
};
Object.assign(Tween.prototype, {
/**
* !#en Sets target's position property according to the bezier curve.
* !#zh 按照贝塞尔路径设置目标的 position 属性。
* @method bezierTo
* @param {number} duration
* @param {cc.Vec2} c1
* @param {cc.Vec2} c2
* @param {cc.Vec2} to
* @return {Tween}
* @typescript bezierTo(duration: number, c1: Vec2, c2: Vec2, to: Vec2): Tween<T>
*/
bezierTo (duration, c1, c2, to, opts) {
let c0x = c1.x, c0y = c1.y,
c1x = c2.x, c1y = c2.y;
opts = opts || Object.create(null);
opts.progress = function (start, end, current, t) {
current.x = bezier(start.x, c0x, c1x, end.x, t);
current.y = bezier(start.y, c0y, c1y, end.y, t);
return current;
}
return this.to(duration, { position: to }, opts);
},
/**
* !#en Sets target's position property according to the bezier curve.
* !#zh 按照贝塞尔路径设置目标的 position 属性。
* @method bezierBy
* @param {number} duration
* @param {cc.Vec2} c1
* @param {cc.Vec2} c2
* @param {cc.Vec2} to
* @return {Tween}
* @typescript bezierBy(duration: number, c1: Vec2, c2: Vec2, to: Vec2): Tween<T>
*/
bezierBy (duration, c1, c2, to, opts) {
let c0x = c1.x, c0y = c1.y,
c1x = c2.x, c1y = c2.y;
opts = opts || Object.create(null);
opts.progress = function (start, end, current, t) {
let sx = start.x, sy = start.y;
current.x = bezier(sx, c0x + sx, c1x + sx, end.x, t);
current.y = bezier(sy, c0y + sy, c1y + sy, end.y, t);
return current;
}
return this.by(duration, { position: to }, opts);
},
/**
* !#en Flips target's scaleX
* !#zh 翻转目标的 scaleX 属性
* @method flipX
* @return {Tween}
* @typescript flipX(): Tween<T>
*/
flipX () {
return this.call(() => { this._target.scaleX *= -1; }, this);
},
/**
* !#en Flips target's scaleY
* !#zh 翻转目标的 scaleY 属性
* @method flipY
* @return {Tween}
* @typescript flipY(): Tween<T>
*/
flipY () {
return this.call(() => { this._target.scaleY *= -1; }, this);
},
/**
* !#en Blinks target by set target's opacity property
* !#zh 通过设置目标的 opacity 属性达到闪烁效果
* @method blink
* @param {number} duration
* @param {number} times
* @param {Object} [opts]
* @param {Function} [opts.progress]
* @param {Function|String} [opts.easing]
* @return {Tween}
* @typescript blink(duration: number, times: number, opts?: {progress?: Function; easing?: Function|string; }): Tween<T>
*/
blink (duration, times, opts) {
var slice = 1.0 / times;
opts = opts || Object.create(null);
opts.progress = function (start, end, current, t) {
if (t >= 1) {
return start;
}
else {
var m = t % slice;
return (m > (slice / 2)) ? 255 : 0;
}
};
return this.to(duration, { opacity: 1 }, opts);
},
})
let tmp_args = [];
function wrapAction (action) {
return function () {
tmp_args.length = 0;
for (let l = arguments.length, i = 0; i < l; i++) {
let arg = tmp_args[i] = arguments[i];
if (arg instanceof Tween) {
tmp_args[i] = arg._union();
}
}
return action.apply(this, tmp_args);
};
}
let actions = {
/**
* !#en
* Add an action which calculate with absolute value
* !#zh
* 添加一个对属性进行绝对值计算的 action
* @method to
* @param {Number} duration
* @param {Object} props - {scale: 2, position: cc.v3(100, 100, 100)}
* @param {Object} [opts]
* @param {Function} [opts.progress]
* @param {Function|String} [opts.easing]
* @return {Tween}
* @typescript
* to<OPTS extends Partial<{ progress: Function, easing: Function | String, onUpdate: Function }>>(duration: number, props: ConstructorType<T>, opts?: OPTS): Tween<T>
*/
to (duration, props, opts) {
opts = opts || Object.create(null);
opts.relative = false;
return new TweenAction(duration, props, opts);
},
/**
* !#en
* Add an action which calculate with relative value
* !#zh
* 添加一个对属性进行相对值计算的 action
* @method by
* @param {Number} duration
* @param {Object} props - {scale: 2, position: cc.v3(100, 100, 100)}
* @param {Object} [opts]
* @param {Function} [opts.progress]
* @param {Function|String} [opts.easing]
* @return {Tween}
* @typescript
* by<OPTS extends Partial<{ progress: Function, easing: Function | String, onUpdate: Function }>>(duration: number, props: ConstructorType<T>, opts?: OPTS): Tween<T>
*/
by (duration, props, opts) {
opts = opts || Object.create(null);
opts.relative = true;
return new TweenAction(duration, props, opts);
},
/**
* !#en
* Directly set target properties
* !#zh
* 直接设置 target 的属性
* @method set
* @param {Object} props
* @return {Tween}
* @typescript
* set (props: ConstructorType<T>) : Tween<T>
*/
set (props) {
return new SetAction(props);
},
/**
* !#en
* Add an delay action
* !#zh
* 添加一个延时 action
* @method delay
* @param {Number} duration
* @return {Tween}
* @typescript delay(duration: number): Tween<T>
*/
delay: cc.delayTime,
/**
* !#en
* Add an callback action
* !#zh
* 添加一个回调 action
* @method call
* @param {Function} callback
* @param {object} [selectTarget]
* @return {Tween}
* @typescript call(callback: Function, selectTarget?: object): Tween<T>
*/
call: cc.callFunc,
/**
* !#en
* Add an hide action
* !#zh
* 添加一个隐藏 action
* @method hide
* @return {Tween}
* @typescript hide(): Tween<T>
*/
hide: cc.hide,
/**
* !#en
* Add an show action
* !#zh
* 添加一个显示 action
* @method show
* @return {Tween}
* @typescript show(): Tween<T>
*/
show: cc.show,
/**
* !#en
* Add an removeSelf action
* !#zh
* 添加一个移除自己 action
* @method removeSelf
* @return {Tween}
* @typescript removeSelf(): Tween<T>
*/
removeSelf: cc.removeSelf,
/**
* !#en
* Add an sequence action
* !#zh
* 添加一个队列 action
* @method sequence
* @param {Action|Tween} action
* @param {Action|Tween} ...actions
* @return {Tween}
* @typescript sequence(action: Action|Tween<T>, ...actions: (Action|Tween<T>)[]): Tween<T>
*/
sequence: wrapAction(cc.sequence),
/**
* !#en
* Add an parallel action
* !#zh
* 添加一个并行 action
* @method parallel
* @param {Action|Tween} action
* @param {Action|Tween} ...actions
* @return {Tween}
* @typescript parallel(action: Action|Tween<T>, ...actions: (Action|Tween<T>)[]): Tween<T>
*/
parallel: wrapAction(cc.spawn)
};
// these action will use previous action as their parameters
let previousAsInputActions = {
/**
* !#en
* Add an repeat action. This action will integrate before actions to a sequence action as their parameters.
* !#zh
* 添加一个重复 action这个 action 会将前一个动作作为他的参数。
* @method repeat
* @param {Number} repeatTimes
* @param {Action | Tween} [action]
* @return {Tween}
* @typescript repeat(repeatTimes: number, action?: Action|Tween<T>): Tween<T>
*/
repeat: cc.repeat,
/**
* !#en
* Add an repeat forever action. This action will integrate before actions to a sequence action as their parameters.
* !#zh
* 添加一个永久重复 action这个 action 会将前一个动作作为他的参数。
* @method repeatForever
* @param {Action | Tween} [action]
* @return {Tween}
* @typescript repeatForever(action?: Action|Tween<T>): Tween<T>
*/
repeatForever: cc.repeatForever,
/**
* !#en
* Add an reverse time action. This action will integrate before actions to a sequence action as their parameters.
* !#zh
* 添加一个倒置时间 action这个 action 会将前一个动作作为他的参数。
* @method reverseTime
* @param {Action | Tween} [action]
* @return {Tween}
* @typescript reverseTime(action?: Action|Tween<T>): Tween<T>
*/
reverseTime: cc.reverseTime,
};
let keys = Object.keys(actions);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
Tween.prototype[key] = function () {
let action = actions[key].apply(this, arguments);
this._actions.push(action);
return this;
};
}
keys = Object.keys(previousAsInputActions);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
Tween.prototype[key] = function () {
let actions = this._actions;
let action = arguments[arguments.length - 1];
let length = arguments.length - 1;
if (action instanceof cc.Tween) {
action = action._union();
}
else if (!(action instanceof cc.Action)) {
action = actions[actions.length - 1];
actions.length -= 1;
length += 1;
}
let args = [action];
for (let i = 0; i < length; i++) {
args.push(arguments[i]);
}
action = previousAsInputActions[key].apply(this, args);
actions.push(action);
return this;
};
}
/**
* @module cc
*/
/**
* @method tween
* @param {Object} [target] - the target to animate
* @return {Tween}
* @typescript
* tween<T> (target?: T) : Tween<T>
*/
cc.tween = function (target) {
return new Tween(target);
};
cc.Tween = Tween;

View File

@@ -0,0 +1,267 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const js = cc.js;
const Playable = require('./playable');
const { EventAnimCurve, EventInfo } = require('./animation-curves');
const WrapModeMask = require('./types').WrapModeMask;
const binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
// The actual animator for Animation Component
function AnimationAnimator (target, animation) {
Playable.call(this);
this.target = target;
this.animation = animation;
this._anims = new js.array.MutableForwardIterator([]);
}
js.extend(AnimationAnimator, Playable);
let p = AnimationAnimator.prototype;
p.playState = function (state, startTime) {
if (!state.clip) {
return;
}
if (!state.curveLoaded) {
initClipData(this.target, state);
}
state.animator = this;
state.play();
if (typeof startTime === 'number') {
state.setTime(startTime);
}
this.play();
};
p.stopStatesExcept = function (state) {
let iterator = this._anims;
let array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
let anim = array[iterator.i];
if (anim === state) {
continue;
}
this.stopState(anim);
}
};
p.addAnimation = function (anim) {
let index = this._anims.array.indexOf(anim);
if (index === -1) {
this._anims.push(anim);
}
anim._setEventTarget(this.animation);
};
p.removeAnimation = function (anim) {
let index = this._anims.array.indexOf(anim);
if (index >= 0) {
this._anims.fastRemoveAt(index);
if (this._anims.array.length === 0) {
this.stop();
}
}
else {
cc.errorID(3907);
}
anim.animator = null;
};
p.sample = function () {
let iterator = this._anims;
let array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
let anim = array[iterator.i];
anim.sample();
}
};
p.stopState = function (state) {
if (state) {
state.stop();
}
};
p.pauseState = function (state) {
if (state) {
state.pause();
}
};
p.resumeState = function (state) {
if (state) {
state.resume();
}
if (this.isPaused) {
this.resume();
}
};
p.setStateTime = function (state, time) {
if (time !== undefined) {
if (state) {
state.setTime(time);
state.sample();
}
}
else {
time = state;
let array = this._anims.array;
for (let i = 0; i < array.length; ++i) {
let anim = array[i];
anim.setTime(time);
anim.sample();
}
}
};
p.onStop = function () {
let iterator = this._anims;
let array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
let anim = array[iterator.i];
anim.stop();
}
};
p.onPause = function () {
let array = this._anims.array;
for (let i = 0; i < array.length; ++i) {
let anim = array[i];
anim.pause();
// need to unbind animator to anim, or it maybe cannot be gc.
anim.animator = null;
}
};
p.onResume = function () {
let array = this._anims.array;
for (let i = 0; i < array.length; ++i) {
let anim = array[i];
// rebind animator to anim
anim.animator = this;
anim.resume();
}
};
p._reloadClip = function (state) {
initClipData(this.target, state);
};
// 这个方法应该是 SampledAnimCurve 才能用
function createBatchedProperty (propPath, firstDotIndex, mainValue, animValue) {
mainValue = mainValue.clone();
let nextValue = mainValue;
let leftIndex = firstDotIndex + 1;
let rightIndex = propPath.indexOf('.', leftIndex);
// scan property path
while (rightIndex !== -1) {
let nextName = propPath.slice(leftIndex, rightIndex);
nextValue = nextValue[nextName];
leftIndex = rightIndex + 1;
rightIndex = propPath.indexOf('.', leftIndex);
}
let lastPropName = propPath.slice(leftIndex);
nextValue[lastPropName] = animValue;
return mainValue;
}
if (CC_TEST) {
cc._Test.createBatchedProperty = createBatchedProperty;
}
function initClipData (root, state) {
let clip = state.clip;
state.duration = clip.duration;
state.speed = clip.speed;
state.wrapMode = clip.wrapMode;
state.frameRate = clip.sample;
if ((state.wrapMode & WrapModeMask.Loop) === WrapModeMask.Loop) {
state.repeatCount = Infinity;
}
else {
state.repeatCount = 1;
}
let curves = state.curves = clip.createCurves(state, root);
// events curve
let events = clip.events;
if (!CC_EDITOR && events) {
let curve;
for (let i = 0, l = events.length; i < l; i++) {
if (!curve) {
curve = new EventAnimCurve();
curve.target = root;
curves.push(curve);
}
let eventData = events[i];
let ratio = eventData.frame / state.duration;
let eventInfo;
let index = binarySearch(curve.ratios, ratio);
if (index >= 0) {
eventInfo = curve.events[index];
}
else {
eventInfo = new EventInfo();
curve.ratios.push(ratio);
curve.events.push(eventInfo);
}
eventInfo.add(eventData.func, eventData.params);
}
}
}
if (CC_TEST) {
cc._Test.initClipData = initClipData;
}
module.exports = AnimationAnimator;

View File

@@ -0,0 +1,308 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const WrapMode = require('./types').WrapMode;
const { DynamicAnimCurve, quickFindIndex } = require('./animation-curves');
const sampleMotionPaths = require('./motion-path-helper').sampleMotionPaths;
const binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
/**
* !#en Class for animation data handling.
* !#zh 动画剪辑,用于存储动画数据。
* @class AnimationClip
* @extends Asset
*/
var AnimationClip = cc.Class({
name: 'cc.AnimationClip',
extends: cc.Asset,
properties: {
_duration: {
default: 0,
type: cc.Float,
},
/**
* !#en Duration of this animation.
* !#zh 动画的持续时间。
* @property duration
* @type {Number}
*/
duration: {
get: function () { return this._duration; },
},
/**
* !#en FrameRate of this animation.
* !#zh 动画的帧速率。
* @property sample
* @type {Number}
*/
sample: {
default: 60,
},
/**
* !#en Speed of this animation.
* !#zh 动画的播放速度。
* @property speed
* @type {Number}
*/
speed: {
default: 1
},
/**
* !#en WrapMode of this animation.
* !#zh 动画的循环模式。
* @property wrapMode
* @type {WrapMode}
*/
wrapMode: {
default: WrapMode.Normal
},
/**
* !#en Curve data.
* !#zh 曲线数据。
* @property curveData
* @type {Object}
* @example {@link cocos2d/core/animation-clip/curve-data.js}
*/
curveData: {
default: {},
visible: false,
},
/**
* !#en Event data.
* !#zh 事件数据。
* @property events
* @type {Object[]}
* @example {@link cocos2d/core/animation-clip/event-data.js}
* @typescript events: {frame: number, func: string, params: string[]}[]
*/
events: {
default: [],
visible: false,
}
},
statics: {
/**
* !#en Crate clip with a set of sprite frames
* !#zh 使用一组序列帧图片来创建动画剪辑
* @method createWithSpriteFrames
* @param {[SpriteFrame]} spriteFrames
* @param {Number} sample
* @return {AnimationClip}
* @static
* @example
*
* var clip = cc.AnimationClip.createWithSpriteFrames(spriteFrames, 10);
*
*/
createWithSpriteFrames: function (spriteFrames, sample) {
if (!Array.isArray(spriteFrames)) {
cc.errorID(3905);
return null;
}
var clip = new AnimationClip();
clip.sample = sample || clip.sample;
clip._duration = spriteFrames.length / clip.sample;
var frames = [];
var step = 1 / clip.sample;
for (var i = 0, l = spriteFrames.length; i < l; i++) {
frames[i] = { frame: (i * step), value: spriteFrames[i] };
}
clip.curveData = {
comps: {
// component
'cc.Sprite': {
// component properties
'spriteFrame': frames
}
}
};
return clip;
}
},
onLoad () {
this._duration = Number.parseFloat(this.duration);
this.speed = Number.parseFloat(this.speed);
this.wrapMode = Number.parseInt(this.wrapMode);
this.frameRate = Number.parseFloat(this.sample);
},
createPropCurve (target, propPath, keyframes) {
let motionPaths = [];
let isMotionPathProp = target instanceof cc.Node && propPath === 'position';
let curve = new DynamicAnimCurve();
// 缓存目标对象,所以 Component 必须一开始都创建好并且不能运行时动态替换……
curve.target = target;
curve.prop = propPath;
// for each keyframes
for (let i = 0, l = keyframes.length; i < l; i++) {
let keyframe = keyframes[i];
let ratio = keyframe.frame / this.duration;
curve.ratios.push(ratio);
if (isMotionPathProp) {
motionPaths.push(keyframe.motionPath);
}
let curveValue = keyframe.value;
curve.values.push(curveValue);
let curveTypes = keyframe.curve;
if (curveTypes) {
if (typeof curveTypes === 'string') {
curve.types.push(curveTypes);
continue;
}
else if (Array.isArray(curveTypes)) {
if (curveTypes[0] === curveTypes[1] &&
curveTypes[2] === curveTypes[3]) {
curve.types.push(DynamicAnimCurve.Linear);
}
else {
curve.types.push(DynamicAnimCurve.Bezier(curveTypes));
}
continue;
}
}
curve.types.push(DynamicAnimCurve.Linear);
}
if (isMotionPathProp) {
sampleMotionPaths(motionPaths, curve, this.duration, this.sample, target);
}
// if every piece of ratios are the same, we can use the quick function to find frame index.
let ratios = curve.ratios;
let currRatioDif, lastRatioDif;
let canOptimize = true;
let EPSILON = 1e-6;
for (let i = 1, l = ratios.length; i < l; i++) {
currRatioDif = ratios[i] - ratios[i-1];
if (i === 1) {
lastRatioDif = currRatioDif;
}
else if (Math.abs(currRatioDif - lastRatioDif) > EPSILON) {
canOptimize = false;
break;
}
}
curve._findFrameIndex = canOptimize ? quickFindIndex : binarySearch;
// find the lerp function
let firstValue = curve.values[0];
if (firstValue !== undefined && firstValue !== null && !curve._lerp) {
if (typeof firstValue === 'number') {
curve._lerp = DynamicAnimCurve.prototype._lerpNumber;
}
else if (firstValue instanceof cc.Quat) {
curve._lerp = DynamicAnimCurve.prototype._lerpQuat;
}
else if (firstValue instanceof cc.Vec2) {
curve._lerp = DynamicAnimCurve.prototype._lerpVector2;
}
else if (firstValue instanceof cc.Vec3) {
curve._lerp = DynamicAnimCurve.prototype._lerpVector3;
}
else if (firstValue.lerp) {
curve._lerp = DynamicAnimCurve.prototype._lerpObject;
}
}
return curve;
},
createTargetCurves (target, curveData, curves) {
let propsData = curveData.props;
let compsData = curveData.comps;
if (propsData) {
for (let propPath in propsData) {
let data = propsData[propPath];
let curve = this.createPropCurve(target, propPath, data);
curves.push(curve);
}
}
if (compsData) {
for (let compName in compsData) {
let comp = target.getComponent(compName);
if (!comp) {
continue;
}
let compData = compsData[compName];
for (let propPath in compData) {
let data = compData[propPath];
let curve = this.createPropCurve(comp, propPath, data);
curves.push(curve);
}
}
}
},
createCurves (state, root) {
let curveData = this.curveData;
let childrenCurveDatas = curveData.paths;
let curves = [];
this.createTargetCurves(root, curveData, curves);
for (let namePath in childrenCurveDatas) {
let target = cc.find(namePath, root);
if (!target) {
continue;
}
let childCurveDatas = childrenCurveDatas[namePath];
this.createTargetCurves(target, childCurveDatas, curves);
}
return curves;
}
});
cc.AnimationClip = module.exports = AnimationClip;

View File

@@ -0,0 +1,464 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const bezierByTime = require('./bezier').bezierByTime;
const binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
const WrapModeMask = require('./types').WrapModeMask;
const WrappedInfo = require('./types').WrappedInfo;
/**
* Compute a new ratio by curve type
* @param {Number} ratio - The origin ratio
* @param {Array|String} type - If it's Array, then ratio will be computed with bezierByTime. If it's string, then ratio will be computed with cc.easing function
*/
function computeRatioByType (ratio, type) {
if (typeof type === 'string') {
var func = cc.easing[type];
if (func) {
ratio = func(ratio);
}
else {
cc.errorID(3906, type);
}
}
else if (Array.isArray(type)) {
// bezier curve
ratio = bezierByTime(type, ratio);
}
return ratio;
}
//
// 动画数据类,相当于 AnimationClip。
// 虽然叫做 AnimCurve但除了曲线可以保存任何类型的值。
//
// @class AnimCurve
//
//
var AnimCurve = cc.Class({
name: 'cc.AnimCurve',
//
// @method sample
// @param {number} time
// @param {number} ratio - The normalized time specified as a number between 0.0 and 1.0 inclusive.
// @param {AnimationState} state
//
sample: function (time, ratio, state) {},
onTimeChangedManually: undefined
});
/**
* 当每两帧之前的间隔都一样的时候可以使用此函数快速查找 index
*/
function quickFindIndex (ratios, ratio) {
var length = ratios.length - 1;
if (length === 0) return 0;
var start = ratios[0];
if (ratio < start) return 0;
var end = ratios[length];
if (ratio > end) return ~ratios.length;
ratio = (ratio - start) / (end - start);
var eachLength = 1 / length;
var index = ratio / eachLength;
var floorIndex = index | 0;
var EPSILON = 1e-6;
if ((index - floorIndex) < EPSILON) {
return floorIndex;
}
else if ((floorIndex + 1 - index) < EPSILON) {
return floorIndex + 1;
}
return ~(floorIndex + 1);
}
//
//
// @class DynamicAnimCurve
//
// @extends AnimCurve
//
var DynamicAnimCurve = cc.Class({
name: 'cc.DynamicAnimCurve',
extends: AnimCurve,
ctor () {
// cache last frame index
this._cachedIndex = 0;
},
properties: {
// The object being animated.
// @property target
// @type {object}
target: null,
// The name of the property being animated.
// @property prop
// @type {string}
prop: '',
// The values of the keyframes. (y)
// @property values
// @type {any[]}
values: [],
// The keyframe ratio of the keyframe specified as a number between 0.0 and 1.0 inclusive. (x)
// @property ratios
// @type {number[]}
ratios: [],
// @property types
// @param {object[]}
// Each array item maybe type:
// - [x, x, x, x]: Four control points for bezier
// - null: linear
types: [],
},
_findFrameIndex: binarySearch,
_lerp: undefined,
_lerpNumber (from, to, t) {
return from + (to - from) * t;
},
_lerpObject (from, to, t) {
return from.lerp(to, t);
},
_lerpQuat: (function () {
let out = cc.quat();
return function (from, to, t) {
return from.lerp(to, t, out);
};
})(),
_lerpVector2: (function () {
let out = cc.v2();
return function (from, to, t) {
return from.lerp(to, t, out);
};
})(),
_lerpVector3: (function () {
let out = cc.v3();
return function (from, to, t) {
return from.lerp(to, t, out);
};
})(),
sample (time, ratio, state) {
let values = this.values;
let ratios = this.ratios;
let frameCount = ratios.length;
if (frameCount === 0) {
return;
}
// only need to refind frame index when ratio is out of range of last from ratio and to ratio.
let shoudRefind = true;
let cachedIndex = this._cachedIndex;
if (cachedIndex < 0) {
cachedIndex = ~cachedIndex;
if (cachedIndex > 0 && cachedIndex < ratios.length) {
let fromRatio = ratios[cachedIndex - 1];
let toRatio = ratios[cachedIndex];
if (ratio > fromRatio && ratio < toRatio) {
shoudRefind = false;
}
}
}
if (shoudRefind) {
this._cachedIndex = this._findFrameIndex(ratios, ratio);
}
// evaluate value
let value;
let index = this._cachedIndex;
if (index < 0) {
index = ~index;
if (index <= 0) {
value = values[0];
}
else if (index >= frameCount) {
value = values[frameCount - 1];
}
else {
var fromVal = values[index - 1];
if (!this._lerp) {
value = fromVal;
}
else {
var fromRatio = ratios[index - 1];
var toRatio = ratios[index];
var type = this.types[index - 1];
var ratioBetweenFrames = (ratio - fromRatio) / (toRatio - fromRatio);
if (type) {
ratioBetweenFrames = computeRatioByType(ratioBetweenFrames, type);
}
// calculate value
var toVal = values[index];
value = this._lerp(fromVal, toVal, ratioBetweenFrames);
}
}
}
else {
value = values[index];
}
this.target[this.prop] = value;
}
});
DynamicAnimCurve.Linear = null;
DynamicAnimCurve.Bezier = function (controlPoints) {
return controlPoints;
};
/**
* Event information,
* @class EventInfo
*
*/
var EventInfo = function () {
this.events = [];
};
/**
* @param {Function} [func] event function
* @param {Object[]} [params] event params
*/
EventInfo.prototype.add = function (func, params) {
this.events.push({
func: func || '',
params: params || []
});
};
/**
*
* @class EventAnimCurve
*
* @extends AnimCurve
*/
var EventAnimCurve = cc.Class({
name: 'cc.EventAnimCurve',
extends: AnimCurve,
properties: {
/**
* The object being animated.
* @property target
* @type {object}
*/
target: null,
/** The keyframe ratio of the keyframe specified as a number between 0.0 and 1.0 inclusive. (x)
* @property ratios
* @type {number[]}
*/
ratios: [],
/**
* @property events
* @type {EventInfo[]}
*/
events: [],
_wrappedInfo: {
default: function () {
return new WrappedInfo();
}
},
_lastWrappedInfo: null,
_ignoreIndex: NaN
},
_wrapIterations: function (iterations) {
if (iterations - (iterations | 0) === 0) iterations -= 1;
return iterations | 0;
},
sample: function (time, ratio, state) {
var length = this.ratios.length;
var currentWrappedInfo = state.getWrappedInfo(state.time, this._wrappedInfo);
var direction = currentWrappedInfo.direction;
var currentIndex = binarySearch(this.ratios, currentWrappedInfo.ratio);
if (currentIndex < 0) {
currentIndex = ~currentIndex - 1;
// if direction is inverse, then increase index
if (direction < 0) currentIndex += 1;
}
if (this._ignoreIndex !== currentIndex) {
this._ignoreIndex = NaN;
}
currentWrappedInfo.frameIndex = currentIndex;
if (!this._lastWrappedInfo) {
this._fireEvent(currentIndex);
this._lastWrappedInfo = new WrappedInfo(currentWrappedInfo);
return;
}
var wrapMode = state.wrapMode;
var currentIterations = this._wrapIterations(currentWrappedInfo.iterations);
var lastWrappedInfo = this._lastWrappedInfo;
var lastIterations = this._wrapIterations(lastWrappedInfo.iterations);
var lastIndex = lastWrappedInfo.frameIndex;
var lastDirection = lastWrappedInfo.direction;
var interationsChanged = lastIterations !== -1 && currentIterations !== lastIterations;
if (lastIndex === currentIndex && interationsChanged && length === 1) {
this._fireEvent(0);
}
else if (lastIndex !== currentIndex || interationsChanged) {
direction = lastDirection;
do {
if (lastIndex !== currentIndex) {
if (direction === -1 && lastIndex === 0 && currentIndex > 0) {
if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) {
direction *= -1;
}
else {
lastIndex = length;
}
lastIterations ++;
}
else if (direction === 1 && lastIndex === length - 1 && currentIndex < length - 1) {
if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) {
direction *= -1;
}
else {
lastIndex = -1;
}
lastIterations ++;
}
if (lastIndex === currentIndex) break;
if (lastIterations > currentIterations) break;
}
lastIndex += direction;
cc.director.getAnimationManager().pushDelayEvent(this, '_fireEvent', [lastIndex]);
} while (lastIndex !== currentIndex && lastIndex > -1 && lastIndex < length);
}
this._lastWrappedInfo.set(currentWrappedInfo);
},
_fireEvent: function (index) {
if (index < 0 || index >= this.events.length || this._ignoreIndex === index) return;
var eventInfo = this.events[index];
var events = eventInfo.events;
if ( !this.target.isValid ) {
return;
}
var components = this.target._components;
for (var i = 0; i < events.length; i++) {
var event = events[i];
var funcName = event.func;
for (var j = 0; j < components.length; j++) {
var component = components[j];
var func = component[funcName];
if (func) func.apply(component, event.params);
}
}
},
onTimeChangedManually: function (time, state) {
this._lastWrappedInfo = null;
this._ignoreIndex = NaN;
var info = state.getWrappedInfo(time, this._wrappedInfo);
var direction = info.direction;
var frameIndex = binarySearch(this.ratios, info.ratio);
// only ignore when time not on a frame index
if (frameIndex < 0) {
frameIndex = ~frameIndex - 1;
// if direction is inverse, then increase index
if (direction < 0) frameIndex += 1;
this._ignoreIndex = frameIndex;
}
}
});
if (CC_TEST) {
cc._Test.DynamicAnimCurve = DynamicAnimCurve;
cc._Test.EventAnimCurve = EventAnimCurve;
cc._Test.quickFindIndex = quickFindIndex;
}
module.exports = {
AnimCurve: AnimCurve,
DynamicAnimCurve: DynamicAnimCurve,
EventAnimCurve: EventAnimCurve,
EventInfo: EventInfo,
computeRatioByType: computeRatioByType,
quickFindIndex: quickFindIndex
};

View File

@@ -0,0 +1,93 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var js = cc.js;
var AnimationManager = cc.Class({
ctor: function () {
this._anims = new js.array.MutableForwardIterator([]);
this._delayEvents = [];
cc.director._scheduler && cc.director._scheduler.enableForTarget(this);
},
// for manager
update: function (dt) {
var iterator = this._anims;
var array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
var anim = array[iterator.i];
if (anim._isPlaying && !anim._isPaused) {
anim.update(dt);
}
}
var events = this._delayEvents;
for (let i = 0; i < events.length; i++) {
var event = events[i];
event.target[event.func].apply(event.target, event.args);
}
events.length = 0;
},
destruct: function () {},
/**
* @param {AnimationState} anim
*/
addAnimation: function (anim) {
var index = this._anims.array.indexOf(anim);
if (index === -1) {
this._anims.push(anim);
}
},
/**
* @param {AnimationState} anim
*/
removeAnimation: function (anim) {
var index = this._anims.array.indexOf(anim);
if (index >= 0) {
this._anims.fastRemoveAt(index);
}
else {
cc.errorID(3907);
}
},
pushDelayEvent: function (target, func, args) {
this._delayEvents.push({
target: target,
func: func,
args: args
});
}
});
cc.AnimationManager = module.exports = AnimationManager;

View File

@@ -0,0 +1,523 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var js = cc.js;
var Playable = require('./playable');
var Types = require('./types');
var WrappedInfo = Types.WrappedInfo;
var WrapMode = Types.WrapMode;
var WrapModeMask = Types.WrapModeMask;
/**
* !#en
* The AnimationState gives full control over animation playback process.
* In most cases the Animation Component is sufficient and easier to use. Use the AnimationState if you need full control.
* !#zh
* AnimationState 完全控制动画播放过程。<br/>
* 大多数情况下 动画组件 是足够和易于使用的。如果您需要更多的动画控制接口,请使用 AnimationState。
* @class AnimationState
* @extends Playable
*
*/
/**
* @method constructor
* @param {AnimationClip} clip
* @param {String} [name]
*/
function AnimationState (clip, name) {
Playable.call(this);
// Mark whether the current frame is played.
// When set new time to animation state, we should ensure the frame at the specified time being played at next update.
this._currentFramePlayed = false;
this._delay = 0;
this._delayTime = 0;
this._wrappedInfo = new WrappedInfo();
this._lastWrappedInfo = null;
this._process = process;
this._clip = clip;
this._name = name || (clip && clip.name);
/**
* @property animator
* @type {AnimationAnimator}
* @private
*/
this.animator = null;
/**
* !#en The curves list.
* !#zh 曲线列表。
* @property curves
* @type {Object[]}
*/
this.curves = [];
// http://www.w3.org/TR/web-animations/#idl-def-AnimationTiming
/**
* !#en The start delay which represents the number of seconds from an animation's start time to the start of
* the active interval.
* !#zh 延迟多少秒播放。
*
* @property delay
* @type {Number}
* @default 0
*/
this.delay = 0;
/**
* !#en The animation's iteration count property.
*
* A real number greater than or equal to zero (including positive infinity) representing the number of times
* to repeat the animation node.
*
* Values less than zero and NaN values are treated as the value 1.0 for the purpose of timing model
* calculations.
*
* !#zh 迭代次数,指动画播放多少次后结束, normalize time。 如 2.52次半
*
* @property repeatCount
* @type {Number}
* @default 1
*/
this.repeatCount = 1;
/**
* !#en The iteration duration of this animation in seconds. (length)
* !#zh 单次动画的持续时间,秒。
*
* @property duration
* @type {Number}
* @readOnly
*/
this.duration = 1;
/**
* !#en The animation's playback speed. 1 is normal playback speed.
* !#zh 播放速率。
* @property speed
* @type {Number}
* @default: 1.0
*/
this.speed = 1;
/**
* !#en
* Wrapping mode of the playing animation.
* Notice : dynamic change wrapMode will reset time and repeatCount property
* !#zh
* 动画循环方式。
* 需要注意的是,动态修改 wrapMode 时,会重置 time 以及 repeatCount
*
* @property wrapMode
* @type {WrapMode}
* @default: WrapMode.Normal
*/
this.wrapMode = WrapMode.Normal;
/**
* !#en The current time of this animation in seconds.
* !#zh 动画当前的时间,秒。
* @property time
* @type {Number}
* @default 0
*/
this.time = 0;
// Animation as event target
this._target = null;
this._lastframeEventOn = false;
this.emit = function () {
var args = new Array(arguments.length);
for (var i = 0, l = args.length; i < l; i++) {
args[i] = arguments[i];
}
cc.director.getAnimationManager().pushDelayEvent(this, '_emit', args);
};
}
js.extend(AnimationState, Playable);
var proto = AnimationState.prototype;
proto._emit = function (type, state) {
if (this._target && this._target.isValid) {
this._target.emit(type, type, state);
}
};
proto.on = function (type, callback, target) {
if (this._target && this._target.isValid) {
if (type === 'lastframe') {
this._lastframeEventOn = true;
}
return this._target.on(type, callback, target);
}
else {
return null;
}
};
proto.once = function (type, callback, target) {
if (this._target && this._target.isValid) {
if (type === 'lastframe') {
this._lastframeEventOn = true;
}
let self = this;
return this._target.once(type, function (event) {
callback.call(target, event);
self._lastframeEventOn = false;
});
}
else {
return null;
}
};
proto.off = function (type, callback, target) {
if (this._target && this._target.isValid) {
if (type === 'lastframe') {
if (!this._target.hasEventListener(type)) {
this._lastframeEventOn = false;
}
}
this._target.off(type, callback, target);
}
};
proto._setEventTarget = function (target) {
this._target = target;
};
proto.onPlay = function () {
// replay
this.setTime(0);
this._delayTime = this._delay;
cc.director.getAnimationManager().addAnimation(this);
if (this.animator) {
this.animator.addAnimation(this);
}
this.emit('play', this);
};
proto.onStop = function () {
if (!this.isPaused) {
cc.director.getAnimationManager().removeAnimation(this);
}
if (this.animator) {
this.animator.removeAnimation(this);
}
this.emit('stop', this);
};
proto.onResume = function () {
cc.director.getAnimationManager().addAnimation(this);
this.emit('resume', this);
};
proto.onPause = function () {
cc.director.getAnimationManager().removeAnimation(this);
this.emit('pause', this);
};
proto.setTime = function (time) {
this._currentFramePlayed = false;
this.time = time || 0;
var curves = this.curves;
for (var i = 0, l = curves.length; i < l; i++) {
var curve = curves[i];
if (curve.onTimeChangedManually) {
curve.onTimeChangedManually(time, this);
}
}
};
function process () {
// sample
var info = this.sample();
if (this._lastframeEventOn) {
var lastInfo;
if (!this._lastWrappedInfo) {
lastInfo = this._lastWrappedInfo = new WrappedInfo(info);
} else {
lastInfo = this._lastWrappedInfo;
}
if (this.repeatCount > 1 && ((info.iterations | 0) > (lastInfo.iterations | 0))) {
this.emit('lastframe', this);
}
lastInfo.set(info);
}
if (info.stopped) {
this.stop();
this.emit('finished', this);
}
}
function simpleProcess () {
var time = this.time;
var duration = this.duration;
if (time > duration) {
time = time % duration;
if (time === 0) time = duration;
}
else if (time < 0) {
time = time % duration;
if (time !== 0) time += duration;
}
var ratio = time / duration;
var curves = this.curves;
for (var i = 0, len = curves.length; i < len; i++) {
var curve = curves[i];
curve.sample(time, ratio, this);
}
if (this._lastframeEventOn) {
if (this._lastIterations === undefined) {
this._lastIterations = ratio;
}
if ((this.time > 0 && this._lastIterations > ratio) || (this.time < 0 && this._lastIterations < ratio)) {
this.emit('lastframe', this);
}
this._lastIterations = ratio;
}
}
proto.update = function (delta) {
// calculate delay time
if (this._delayTime > 0) {
this._delayTime -= delta;
if (this._delayTime > 0) {
// still waiting
return;
}
}
// make first frame perfect
//var playPerfectFirstFrame = (this.time === 0);
if (this._currentFramePlayed) {
this.time += (delta * this.speed);
}
else {
this._currentFramePlayed = true;
}
this._process();
};
proto._needRevers = function (currentIterations) {
var wrapMode = this.wrapMode;
var needRevers = false;
if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) {
var isEnd = currentIterations - (currentIterations | 0) === 0;
if (isEnd && (currentIterations > 0)) {
currentIterations -= 1;
}
var isOddIteration = currentIterations & 1;
if (isOddIteration) {
needRevers = !needRevers;
}
}
if ((wrapMode & WrapModeMask.Reverse) === WrapModeMask.Reverse) {
needRevers = !needRevers;
}
return needRevers;
};
proto.getWrappedInfo = function (time, info) {
info = info || new WrappedInfo();
var stopped = false;
var duration = this.duration;
var repeatCount = this.repeatCount;
var currentIterations = time > 0 ? (time / duration) : -(time / duration);
if (currentIterations >= repeatCount) {
currentIterations = repeatCount;
stopped = true;
var tempRatio = repeatCount - (repeatCount | 0);
if (tempRatio === 0) {
tempRatio = 1; // 如果播放过,动画不复位
}
time = tempRatio * duration * (time > 0 ? 1 : -1);
}
if (time > duration) {
var tempTime = time % duration;
time = tempTime === 0 ? duration : tempTime;
}
else if (time < 0) {
time = time % duration;
if (time !== 0 ) time += duration;
}
var needRevers = false;
var shouldWrap = this._wrapMode & WrapModeMask.ShouldWrap;
if (shouldWrap) {
needRevers = this._needRevers(currentIterations);
}
var direction = needRevers ? -1 : 1;
if (this.speed < 0) {
direction *= -1;
}
// calculate wrapped time
if (shouldWrap && needRevers) {
time = duration - time;
}
info.ratio = time / duration;
info.time = time;
info.direction = direction;
info.stopped = stopped;
info.iterations = currentIterations;
return info;
};
proto.sample = function () {
var info = this.getWrappedInfo(this.time, this._wrappedInfo);
var curves = this.curves;
for (var i = 0, len = curves.length; i < len; i++) {
var curve = curves[i];
curve.sample(info.time, info.ratio, this);
}
return info;
};
/**
* !#en The clip that is being played by this animation state.
* !#zh 此动画状态正在播放的剪辑。
* @property clip
* @type {AnimationClip}
* @final
*/
js.get(proto, 'clip', function () {
return this._clip;
});
/**
* !#en The name of the playing animation.
* !#zh 动画的名字
* @property name
* @type {String}
* @readOnly
*/
js.get(proto, 'name', function () {
return this._name;
});
js.obsolete(proto, 'AnimationState.length', 'duration');
js.getset(proto, 'curveLoaded',
function () {
return this.curves.length > 0;
},
function () {
this.curves.length = 0;
}
);
js.getset(proto, 'wrapMode',
function () {
return this._wrapMode;
},
function (value) {
this._wrapMode = value;
if (CC_EDITOR) return;
// dynamic change wrapMode will need reset time to 0
this.time = 0;
if (value & WrapModeMask.Loop) {
this.repeatCount = Infinity;
}
else {
this.repeatCount = 1;
}
}
);
js.getset(proto, 'repeatCount',
function () {
return this._repeatCount;
},
function (value) {
this._repeatCount = value;
var shouldWrap = this._wrapMode & WrapModeMask.ShouldWrap;
var reverse = (this.wrapMode & WrapModeMask.Reverse) === WrapModeMask.Reverse;
if (value === Infinity && !shouldWrap && !reverse) {
this._process = simpleProcess;
}
else {
this._process = process;
}
}
);
js.getset(proto, 'delay',
function () {
return this._delay;
},
function (value) {
this._delayTime = this._delay = value;
}
);
cc.AnimationState = module.exports = AnimationState;

View File

@@ -0,0 +1,213 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
//var bezier = (function () {
// function B1 (t) { return (t * t * t); }
// function B2 (t) { return (3 * t * t * (1 - t)); }
// function B3 (t) { return (3 * t * (1 - t) * (1 - t)); }
// function B4 (t) { return ((1 - t) * (1 - t) * (1 - t)); }
// function bezier (C1, C2, C3, C4, t) {
// return C1 * B1(t) + C2 * B2(t) + C3 * B3(t) + C4 * B4(t);
// }
//
// //function bezier (C1, C2, C3, C4, t, out) {
// // out.x = C1.x * B1(t) + C2.x * B2(t) + C3.x * B3(t) + C4.x * B4(t);
// // out.y = C1.y * B1(t) + C2.y * B2(t) + C3.y * B3(t) + C4.y * B4(t);
// //}
//
// return bezier;
//})();
function bezier (C1, C2, C3, C4, t) {
var t1 = 1 - t;
return t1 * (t1 * (C1 + (C2 * 3 - C1) * t) + C3 * 3 * t * t) + C4 * t * t * t;
}
//function bezier (c0, c1, c2, c3, t) {
// var cy = 3.0 * (c1);
// var by = 3.0 * (c3 - c1) - cy;
// var ay = 1 - cy - by;
// return (ay * t * t * t) + (by * t * t) + (cy * t);
//}
//var sin = Math.sin;
var cos = Math.cos,
acos = Math.acos,
max = Math.max,
//atan2 = Math.atan2,
pi = Math.PI,
tau = 2 * pi,
sqrt = Math.sqrt;
function crt (v) {
if (v < 0) {
return -Math.pow(-v, 1 / 3);
}
else {
return Math.pow(v, 1 / 3);
}
}
//function align (curve, line) {
// var tx = line.p1.x,
// ty = line.p1.y,
// a = -atan2(line.p2.y-ty, line.p2.x-tx);
// curve = [{x:0, y:1}, {x: curve[0], y: 1-curve[1]}, {x: curve[2], y: 1-curve[3]}, {x:1, y:0}];
// return curve.map(function(v) {
// return {
// x: (v.x-tx)*cos(a) - (v.y-ty)*sin(a),
// y: (v.x-tx)*sin(a) + (v.y-ty)*cos(a)
// };
// });
//}
// Modified from http://jsbin.com/yibipofeqi/1/edit, optimized for animations.
// The origin Cardano's algorithm is based on http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm
function cardano (curve, x) {
// align curve with the intersecting line:
//var line = {p1: {x: x, y: 0}, p2: {x: x, y: 1}};
//var aligned = align(curve, line);
//// and rewrite from [a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3] form
// pa = aligned[0].y,
// pb = aligned[1].y,
// pc = aligned[2].y,
// pd = aligned[3].y;
////// curve = [{x:0, y:1}, {x: curve[0], y: 1-curve[1]}, {x: curve[2], y: 1-curve[3]}, {x:1, y:0}];
var pa = x - 0;
var pb = x - curve[0];
var pc = x - curve[2];
var pd = x - 1;
// to [t^3 + at^2 + bt + c] form:
var pa3 = pa * 3;
var pb3 = pb * 3;
var pc3 = pc * 3;
var d = (-pa + pb3 - pc3 + pd),
rd = 1 / d,
r3 = 1 / 3,
a = (pa3 - 6 * pb + pc3) * rd,
a3 = a * r3,
b = (-pa3 + pb3) * rd,
c = pa * rd,
// then, determine p and q:
p = (3 * b - a * a) * r3,
p3 = p * r3,
q = (2 * a * a * a - 9 * a * b + 27 * c) / 27,
q2 = q / 2,
// and determine the discriminant:
discriminant = q2 * q2 + p3 * p3 * p3,
// and some reserved variables
u1, v1, x1, x2, x3;
// If the discriminant is negative, use polar coordinates
// to get around square roots of negative numbers
if (discriminant < 0) {
var mp3 = -p * r3,
mp33 = mp3 * mp3 * mp3,
r = sqrt(mp33),
// compute cosphi corrected for IEEE float rounding:
t = -q / (2 * r),
cosphi = t < -1 ? -1 : t > 1 ? 1 : t,
phi = acos(cosphi),
crtr = crt(r),
t1 = 2 * crtr;
x1 = t1 * cos(phi * r3) - a3;
x2 = t1 * cos((phi + tau) * r3) - a3;
x3 = t1 * cos((phi + 2 * tau) * r3) - a3;
// choose best percentage
if (0 <= x1 && x1 <= 1) {
if (0 <= x2 && x2 <= 1) {
if (0 <= x3 && x3 <= 1) {
return max(x1, x2, x3);
}
else {
return max(x1, x2);
}
}
else if (0 <= x3 && x3 <= 1) {
return max(x1, x3);
}
else {
return x1;
}
}
else {
if (0 <= x2 && x2 <= 1) {
if (0 <= x3 && x3 <= 1) {
return max(x2, x3);
}
else {
return x2;
}
}
else {
return x3;
}
}
}
else if (discriminant === 0) {
u1 = q2 < 0 ? crt(-q2) : -crt(q2);
x1 = 2 * u1 - a3;
x2 = -u1 - a3;
// choose best percentage
if (0 <= x1 && x1 <= 1) {
if (0 <= x2 && x2 <= 1) {
return max(x1, x2);
}
else {
return x1;
}
}
else {
return x2;
}
}
// one real root, and two imaginary roots
else {
var sd = sqrt(discriminant);
u1 = crt(-q2 + sd);
v1 = crt(q2 + sd);
x1 = u1 - v1 - a3;
return x1;
}
}
function bezierByTime (controlPoints, x) {
var percent = cardano(controlPoints, x); // t
var p1y = controlPoints[1]; // b
var p2y = controlPoints[3]; // c
// return bezier(0, p1y, p2y, 1, percent);
return ((1 - percent) * (p1y + (p2y - p1y) * percent) * 3 + percent * percent) * percent;
}
if (CC_TEST) {
cc._Test.bezier = bezier;
cc._Test.bezierByTime = bezierByTime;
}
module.exports = {
bezier: bezier,
bezierByTime: bezierByTime
};

View File

@@ -0,0 +1,538 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc
*/
/**
* !#en
* This class provide easing methods for {{#crossLink "tween"}}{{/crossLink}} class.<br>
* Demonstratio: https://easings.net/
* !#zh
* 缓动函数类,为 {{#crossLink "Tween"}}{{/crossLink}} 提供缓动效果函数。<br>
* 函数效果演示: https://easings.net/
* @class Easing
*/
var easing = {
constant: function () { return 0; },
linear: function (k) { return k; },
// quad
// easing equation function for a quadratic (t^2)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with quadratic formula. From slow to fast.
* !#zh 平方曲线缓入函数。运动由慢到快。
* @method quadIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value
*/
quadIn: function (k) { return k * k; },
/**
* !#en Easing out with quadratic formula. From fast to slow.
* !#zh 平方曲线缓出函数。运动由快到慢。
* @method quadOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value
*/
quadOut: function (k) { return k * ( 2 - k ); },
/**
* !#en Easing in and out with quadratic formula. From slow to fast, then back to slow.
* !#zh 平方曲线缓入缓出函数。运动由慢到快再到慢。
* @method quadInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value
*/
quadInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k;
}
return -0.5 * ( --k * ( k - 2 ) - 1 );
},
// cubic
// easing equation function for a cubic (t^3)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with cubic formula. From slow to fast.
* !#zh 立方曲线缓入函数。运动由慢到快。
* @method cubicIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
cubicIn: function (k) { return k * k * k; },
/**
* !#en Easing out with cubic formula. From slow to fast.
* !#zh 立方曲线缓出函数。运动由快到慢。
* @method cubicOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
cubicOut: function (k) { return --k * k * k + 1; },
/**
* !#en Easing in and out with cubic formula. From slow to fast, then back to slow.
* !#zh 立方曲线缓入缓出函数。运动由慢到快再到慢。
* @method cubicInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
cubicInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k * k;
}
return 0.5 * ( ( k -= 2 ) * k * k + 2 );
},
// quart
// easing equation function for a quartic (t^4)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with quartic formula. From slow to fast.
* !#zh 四次方曲线缓入函数。运动由慢到快。
* @method quartIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quartIn: function (k) { return k * k * k * k; },
/**
* !#en Easing out with quartic formula. From fast to slow.
* !#zh 四次方曲线缓出函数。运动由快到慢。
* @method quartOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quartOut: function (k) { return 1 - ( --k * k * k * k ); },
/**
* !#en Easing in and out with quartic formula. From slow to fast, then back to slow.
* !#zh 四次方曲线缓入缓出函数。运动由慢到快再到慢。
* @method quartInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quartInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k * k * k;
}
return -0.5 * ( ( k -= 2 ) * k * k * k - 2 );
},
// quint
// easing equation function for a quintic (t^5)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with quintic formula. From slow to fast.
* !#zh 五次方曲线缓入函数。运动由慢到快。
* @method quintIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quintIn: function (k) { return k * k * k * k * k; },
/**
* !#en Easing out with quintic formula. From fast to slow.
* !#zh 五次方曲线缓出函数。运动由快到慢。
* @method quintOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quintOut: function (k) { return --k * k * k * k * k + 1; },
/**
* !#en Easing in and out with quintic formula. From slow to fast, then back to slow.
* !#zh 五次方曲线缓入缓出函数。运动由慢到快再到慢。
* @method quintInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quintInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k * k * k * k;
}
return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 );
},
// sine
// easing equation function for a sinusoidal (sin(t))
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in and out with sine formula. From slow to fast.
* !#zh 正弦曲线缓入函数。运动由慢到快。
* @method sineIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
sineIn: function (k) { return 1 - Math.cos(k * Math.PI / 2); },
/**
* !#en Easing in and out with sine formula. From fast to slow.
* !#zh 正弦曲线缓出函数。运动由快到慢。
* @method sineOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
sineOut: function (k) { return Math.sin(k * Math.PI / 2); },
/**
* !#en Easing in and out with sine formula. From slow to fast, then back to slow.
* !#zh 正弦曲线缓入缓出函数。运动由慢到快再到慢。
* @method sineInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
sineInOut: function (k) { return 0.5 * ( 1 - Math.cos(Math.PI * k) ); },
// expo
// easing equation function for an exponential (2^t)
// param t: Current time (in frames or seconds).
// return: The correct value.
/**
* !#en Easing in and out with exponential formula. From slow to fast.
* !#zh 指数曲线缓入函数。运动由慢到快。
* @method expoIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
expoIn: function (k) { return k === 0 ? 0 : Math.pow(1024, k - 1); },
/**
* !#en Easing in and out with exponential formula. From fast to slow.
* !#zh 指数曲线缓出函数。运动由快到慢。
* @method expoOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
expoOut: function (k) { return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); },
/**
* !#en Easing in and out with exponential formula. From slow to fast.
* !#zh 指数曲线缓入和缓出函数。运动由慢到很快再到慢。
* @method expoInOut
* @param {Number} t The current time as a percentage of the total time, then back to slow.
* @return {Number} The correct value.
*/
expoInOut: function (k) {
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (( k *= 2 ) < 1) {
return 0.5 * Math.pow(1024, k - 1);
}
return 0.5 * ( -Math.pow(2, -10 * ( k - 1 )) + 2 );
},
// circ
// easing equation function for a circular (sqrt(1-t^2))
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in and out with circular formula. From slow to fast.
* !#zh 循环公式缓入函数。运动由慢到快。
* @method circIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
circIn: function (k) { return 1 - Math.sqrt(1 - k * k); },
/**
* !#en Easing in and out with circular formula. From fast to slow.
* !#zh 循环公式缓出函数。运动由快到慢。
* @method circOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
circOut: function (k) { return Math.sqrt(1 - ( --k * k )); },
/**
* !#en Easing in and out with circular formula. From slow to fast.
* !#zh 指数曲线缓入缓出函数。运动由慢到很快再到慢。
* @method circInOut
* @param {Number} t The current time as a percentage of the total time, then back to slow.
* @return {Number} The correct value.
*/
circInOut: function (k) {
if (( k *= 2 ) < 1) {
return -0.5 * ( Math.sqrt(1 - k * k) - 1);
}
return 0.5 * ( Math.sqrt(1 - ( k -= 2) * k) + 1);
},
// elastic
// easing equation function for an elastic (exponentially decaying sine wave)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
// recommand value: elastic (t)
/**
* !#en Easing in action with a spring oscillating effect.
* !#zh 弹簧回震效果的缓入函数。
* @method elasticIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
elasticIn: function (k) {
var s, a = 0.1, p = 0.4;
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (!a || a < 1) {
a = 1;
s = p / 4;
}
else {
s = p * Math.asin(1 / a) / ( 2 * Math.PI );
}
return -( a * Math.pow(2, 10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) );
},
/**
* !#en Easing out action with a spring oscillating effect.
* !#zh 弹簧回震效果的缓出函数。
* @method elasticOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
elasticOut: function (k) {
var s, a = 0.1, p = 0.4;
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (!a || a < 1) {
a = 1;
s = p / 4;
}
else {
s = p * Math.asin(1 / a) / ( 2 * Math.PI );
}
return ( a * Math.pow(2, -10 * k) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) + 1 );
},
/**
* !#en Easing in and out action with a spring oscillating effect.
* !#zh 弹簧回震效果的缓入缓出函数。
* @method elasticInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
elasticInOut: function (k) {
var s, a = 0.1, p = 0.4;
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (!a || a < 1) {
a = 1;
s = p / 4;
}
else {
s = p * Math.asin(1 / a) / ( 2 * Math.PI );
}
if (( k *= 2 ) < 1) {
return -0.5 *
( a * Math.pow(2, 10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) );
}
return a * Math.pow(2, -10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) * 0.5 + 1;
},
// back
// easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in action with "back up" behavior.
* !#zh 回退效果的缓入函数。
* @method backIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
backIn: function (k) {
var s = 1.70158;
return k * k * ( ( s + 1 ) * k - s );
},
/**
* !#en Easing out action with "back up" behavior.
* !#zh 回退效果的缓出函数。
* @method backOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
backOut: function (k) {
var s = 1.70158;
return --k * k * ( ( s + 1 ) * k + s ) + 1;
},
/**
* !#en Easing in and out action with "back up" behavior.
* !#zh 回退效果的缓入缓出函数。
* @method backInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
backInOut: function (k) {
var s = 1.70158 * 1.525;
if (( k *= 2 ) < 1) {
return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) );
}
return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 );
},
// bounce
// easing equation function for a bounce (exponentially decaying parabolic bounce)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in action with bouncing effect.
* !#zh 弹跳效果的缓入函数。
* @method bounceIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
bounceIn: function (k) {
return 1 - easing.bounceOut(1 - k);
},
/**
* !#en Easing out action with bouncing effect.
* !#zh 弹跳效果的缓出函数。
* @method bounceOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
bounceOut: function (k) {
if (k < ( 1 / 2.75 )) {
return 7.5625 * k * k;
}
else if (k < ( 2 / 2.75 )) {
return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
}
else if (k < ( 2.5 / 2.75 )) {
return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
}
else {
return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
}
},
/**
* !#en Easing in and out action with bouncing effect.
* !#zh 弹跳效果的缓入缓出函数。
* @method bounceInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
bounceInOut: function (k) {
if (k < 0.5) {
return easing.bounceIn(k * 2) * 0.5;
}
return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
},
/**
* !#en Target will run action with smooth effect.
* !#zh 平滑效果函数。
* @method smooth
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
// t<=0: 0 | 0<t<1: 3*t^2 - 2*t^3 | t>=1: 1
smooth: function (t) {
if (t <= 0) {
return 0;
}
if (t >= 1) {
return 1;
}
return t * t * (3 - 2 * t);
},
/**
* !#en Target will run action with fade effect.
* !#zh 渐褪效果函数。
* @method fade
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
// t<=0: 0 | 0<t<1: 6*t^5 - 15*t^4 + 10*t^3 | t>=1: 1
fade: function (t) {
if (t <= 0) {
return 0;
}
if (t >= 1) {
return 1;
}
return t * t * t * (t * (t * 6 - 15) + 10);
},
};
function _makeOutIn (fnIn, fnOut) {
return function (k) {
if (k < 0.5) {
return fnOut(k * 2) / 2;
}
return fnIn(2 * k - 1) / 2 + 0.5;
};
}
easing.quadOutIn = _makeOutIn(easing.quadIn, easing.quadOut);
easing.cubicOutIn = _makeOutIn(easing.cubicIn, easing.cubicOut);
easing.quartOutIn = _makeOutIn(easing.quartIn, easing.quartOut);
easing.quintOutIn = _makeOutIn(easing.quintIn, easing.quintOut);
easing.sineOutIn = _makeOutIn(easing.sineIn, easing.sineOut);
easing.expoOutIn = _makeOutIn(easing.expoIn, easing.expoOut);
easing.circOutIn = _makeOutIn(easing.circIn, easing.circOut);
easing.backOutIn = _makeOutIn(easing.backIn, easing.backOut);
easing.bounceIn = function (k) { return 1 - easing.bounceOut(1 - k); };
easing.bounceInOut = function (k) {
if (k < 0.5) {
return easing.bounceIn(k * 2) * 0.5;
}
return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
};
easing.bounceOutIn = _makeOutIn(easing.bounceIn, easing.bounceOut);
/**
* @module cc
*/
/**
* !#en This is a Easing instance.
* !#zh 这是一个 Easing 类实例。
* @property easing
* @type Easing
*/
cc.easing = module.exports = easing;

View File

@@ -0,0 +1,34 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
require('./bezier');
require('./easing');
require('./types');
require('./motion-path-helper');
require('./animation-curves');
require('./animation-clip');
require('./animation-manager');
require('./animation-state');
require('./animation-animator');

View File

@@ -0,0 +1,415 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var DynamicAnimCurve = require('./animation-curves').DynamicAnimCurve;
var computeRatioByType = require('./animation-curves').computeRatioByType;
var bezier = require('./bezier').bezier;
var binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
var v2 = cc.v2;
function Curve (points) {
this.points = points || [];
this.beziers = [];
this.ratios = [];
this.progresses = [];
this.length = 0;
this.computeBeziers();
}
Curve.prototype.computeBeziers = function () {
this.beziers.length = 0;
this.ratios.length = 0;
this.progresses.length = 0;
this.length = 0;
var bezier;
for (var i = 1; i < this.points.length; i++) {
var startPoint = this.points[i - 1];
var endPoint = this.points[i];
bezier = new Bezier();
bezier.start = startPoint.pos;
bezier.startCtrlPoint = startPoint.out;
bezier.end = endPoint.pos;
bezier.endCtrlPoint = endPoint.in;
this.beziers.push(bezier);
this.length += bezier.getLength();
}
var current = 0;
for (var i = 0; i < this.beziers.length; i++) {
bezier = this.beziers[i];
this.ratios[i] = bezier.getLength() / this.length;
this.progresses[i] = current = current + this.ratios[i];
}
return this.beziers;
};
function Bezier () {
this.start = v2();
this.end = v2();
this.startCtrlPoint = v2(); // cp0, cp1
this.endCtrlPoint = v2(); // cp2, cp3
}
// Get point at relative position in curve according to arc length
// - u [0 .. 1]
Bezier.prototype.getPointAt = function ( u ) {
var t = this.getUtoTmapping( u );
return this.getPoint( t );
};
// Get point at time t
// - t [0 .. 1]
Bezier.prototype.getPoint = function ( t ) {
var x = bezier(this.start.x, this.startCtrlPoint.x, this.endCtrlPoint.x, this.end.x, t);
var y = bezier(this.start.y, this.startCtrlPoint.y, this.endCtrlPoint.y, this.end.y, t);
return new v2(x, y);
};
// Get total curve arc length
Bezier.prototype.getLength = function () {
var lengths = this.getLengths();
return lengths[ lengths.length - 1 ];
};
// Get list of cumulative segment lengths
Bezier.prototype.getLengths = function ( divisions ) {
if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
if ( this.cacheArcLengths
&& ( this.cacheArcLengths.length === divisions + 1 )) {
//console.log( "cached", this.cacheArcLengths );
return this.cacheArcLengths;
}
var cache = [];
var current, last = this.getPoint( 0 ), vector = v2();
var p, sum = 0;
cache.push( 0 );
for ( p = 1; p <= divisions; p ++ ) {
current = this.getPoint ( p / divisions );
vector.x = last.x - current.x;
vector.y = last.y - current.y;
sum += vector.mag();
cache.push( sum );
last = current;
}
this.cacheArcLengths = cache;
return cache; // { sums: cache, sum:sum }; Sum is in the last element.
};
Bezier.prototype.getUtoTmapping = function ( u, distance ) {
var arcLengths = this.getLengths();
var i = 0, il = arcLengths.length;
var targetArcLength; // The targeted u distance value to get
if ( distance ) {
targetArcLength = distance;
} else {
targetArcLength = u * arcLengths[ il - 1 ];
}
//var time = Date.now();
// binary search for the index with largest value smaller than target u distance
var low = 0, high = il - 1, comparison;
while ( low <= high ) {
i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
comparison = arcLengths[ i ] - targetArcLength;
if ( comparison < 0 ) {
low = i + 1;
continue;
} else if ( comparison > 0 ) {
high = i - 1;
continue;
} else {
high = i;
break;
// DONE
}
}
i = high;
//console.log('b' , i, low, high, Date.now()- time);
if ( arcLengths[ i ] === targetArcLength ) {
var t = i / ( il - 1 );
return t;
}
// we could get finer grain at lengths, or use simple interpolatation between two points
var lengthBefore = arcLengths[ i ];
var lengthAfter = arcLengths[ i + 1 ];
var segmentLength = lengthAfter - lengthBefore;
// determine where we are between the 'before' and 'after' points
var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
// add that fractional amount to t
var t = ( i + segmentFraction ) / ( il -1 );
return t;
};
function checkMotionPath(motionPath) {
if (!Array.isArray(motionPath)) return false;
for (let i = 0, l = motionPath.length; i < l; i++) {
let controls = motionPath[i];
if (!Array.isArray(controls) || controls.length !== 6) return false;
}
return true;
}
function sampleMotionPaths (motionPaths, data, duration, fps, target) {
function createControlPoints(array) {
if (array instanceof cc.Vec2) {
return {
in: array,
pos: array,
out: array
};
}
else if (Array.isArray(array) && array.length === 6) {
return {
in: v2(array[2], array[3]),
pos: v2(array[0], array[1]),
out: v2(array[4], array[5])
};
}
return {
in: cc.Vec2.ZERO,
pos: cc.Vec2.ZERO,
out: cc.Vec2.ZERO
};
}
let values = data.values = data.values.map(function (value) {
if (Array.isArray(value)) {
value = value.length === 2 ? cc.v2(value[0], value[1]) : cc.v3(value[0], value[1], value[2]);
}
return value;
});
if (motionPaths.length === 0 || values.length === 0) {
return;
}
let motionPathValid = false;
for (let i = 0; i < motionPaths.length; i++) {
let motionPath = motionPaths[i];
if (motionPath && !checkMotionPath(motionPath)) {
cc.errorID(3904, target ? target.name : '', 'position', i);
motionPath = null;
}
if (motionPath && motionPath.length > 0) {
motionPathValid = true;
break;
}
}
if (!motionPathValid) {
return;
}
if (values.length === 1) {
return;
}
var types = data.types;
var ratios = data.ratios;
var newValues = data.values = [];
var newTypes = data.types = [];
var newRatios = data.ratios = [];
function addNewDatas (value, type, ratio) {
newValues.push(value);
newTypes.push(type);
newRatios.push(ratio);
}
// ensure every ratio section's length is the same
var startRatioOffset = 0;
var EPSILON = 1e-6;
var newType = DynamicAnimCurve.Linear;
// do not need to compute last path
for (var i = 0, l = motionPaths.length; i < l-1; i++) {
var motionPath = motionPaths[i];
var ratio = ratios[i];
var nextRatio = ratios[i + 1];
var betweenRatio = nextRatio - ratio;
var value = values[i];
var nextValue = values[i + 1];
var type = types[i];
var results = [];
var progress = startRatioOffset / betweenRatio;
var speed = 1 / (betweenRatio * duration * fps);
var finalProgress;
if (motionPath && motionPath.length > 0) {
var points = [];
points.push(createControlPoints(value));
for (var j = 0, l2 = motionPath.length; j < l2; j++) {
var controlPoints = createControlPoints(motionPath[j]);
points.push(controlPoints);
}
points.push(createControlPoints(nextValue));
// create Curve to compute beziers
var curve = new Curve(points);
curve.computeBeziers();
// sample beziers
var progresses = curve.progresses;
while ( 1 - progress > EPSILON) {
finalProgress = progress;
finalProgress = computeRatioByType(finalProgress, type);
var pos, bezier, normal, length;
if (finalProgress < 0) {
bezier = curve.beziers[0];
length = (0 - finalProgress) * bezier.getLength();
normal = bezier.start.sub(bezier.endCtrlPoint).normalize();
pos = bezier.start.add(normal.mul(length));
}
else if (finalProgress > 1) {
bezier = curve.beziers[curve.beziers.length - 1];
length = (finalProgress - 1) * bezier.getLength();
normal = bezier.end.sub(bezier.startCtrlPoint).normalize();
pos = bezier.end.add(normal.mul(length));
}
else {
var bezierIndex = binarySearch(progresses, finalProgress);
if (bezierIndex < 0) bezierIndex = ~bezierIndex;
finalProgress -= bezierIndex > 0 ? progresses[bezierIndex - 1] : 0;
finalProgress = finalProgress / curve.ratios[bezierIndex];
pos = curve.beziers[bezierIndex].getPointAt(finalProgress);
}
results.push(pos);
progress += speed;
}
}
else {
while ( 1 - progress > EPSILON) {
finalProgress = progress;
finalProgress = computeRatioByType(finalProgress, type);
results.push(value.lerp(nextValue, finalProgress));
progress += speed;
}
}
newType = type === 'constant' ? type : DynamicAnimCurve.Linear;
for (var j = 0, l2 = results.length; j < l2; j++) {
var newRatio = ratio + startRatioOffset + speed * j * betweenRatio;
addNewDatas(results[j], newType, newRatio);
}
if (Math.abs(progress - 1) > EPSILON) // progress > 1
startRatioOffset = (progress - 1) * betweenRatio;
else
startRatioOffset = 0;
}
if (ratios[ratios.length - 1] !== newRatios[newRatios.length -1]) {
addNewDatas(values[values.length - 1], newType, ratios[ratios.length - 1]);
}
}
if (CC_TEST) {
cc._Test.sampleMotionPaths = sampleMotionPaths;
}
module.exports = {
sampleMotionPaths: sampleMotionPaths,
Curve: Curve,
Bezier: Bezier
};

View File

@@ -0,0 +1,170 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var js = cc.js;
const debug = require('../core/CCDebug');
/**
* @class Playable
*
*/
function Playable () {
this._isPlaying = false;
this._isPaused = false;
this._stepOnce = false;
}
var prototype = Playable.prototype;
/**
* !#en Is playing or paused in play mode?
* !#zh 当前是否正在播放。
* @property isPlaying
* @type {boolean}
* @default false
* @readOnly
*/
js.get(prototype, 'isPlaying', function () {
return this._isPlaying;
}, true);
/**
* !#en Is currently paused? This can be true even if in edit mode(isPlaying == false).
* !#zh 当前是否正在暂停
* @property isPaused
* @type {boolean}
* @default false
* @readOnly
*/
js.get(prototype, 'isPaused', function () {
return this._isPaused;
}, true);
// virtual
var virtual = function () {};
/**
* @method onPlay
* @private
*/
prototype.onPlay = virtual;
/**
* @method onPause
* @private
*/
prototype.onPause = virtual;
/**
* @method onResume
* @private
*/
prototype.onResume = virtual;
/**
* @method onStop
* @private
*/
prototype.onStop = virtual;
/**
* @method onError
* @param {string} errorCode
* @private
*/
prototype.onError = virtual;
// public
/**
* !#en Play this animation.
* !#zh 播放动画。
* @method play
*/
prototype.play = function () {
if (this._isPlaying) {
if (this._isPaused) {
this._isPaused = false;
this.onResume();
}
else {
this.onError(debug.getError(3912));
}
}
else {
this._isPlaying = true;
this.onPlay();
}
};
/**
* !#en Stop this animation.
* !#zh 停止动画播放。
* @method stop
*/
prototype.stop = function () {
if (this._isPlaying) {
this._isPlaying = false;
this.onStop();
// need reset pause flag after onStop
this._isPaused = false;
}
};
/**
* !#en Pause this animation.
* !#zh 暂停动画。
* @method pause
*/
prototype.pause = function () {
if (this._isPlaying && !this._isPaused) {
this._isPaused = true;
this.onPause();
}
};
/**
* !#en Resume this animation.
* !#zh 重新播放动画。
* @method resume
*/
prototype.resume = function () {
if (this._isPlaying && this._isPaused) {
this._isPaused = false;
this.onResume();
}
};
/**
* !#en Perform a single frame step.
* !#zh 执行一帧动画。
* @method step
*/
prototype.step = function () {
this.pause();
this._stepOnce = true;
if (!this._isPlaying) {
this.play();
}
};
module.exports = Playable;

View File

@@ -0,0 +1,125 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var WrapModeMask = {
Loop: 1 << 1,
ShouldWrap: 1 << 2,
// Reserved: 1 << 3,
PingPong: 1 << 4 | 1 << 1 | 1 << 2, // Loop, ShouldWrap
Reverse: 1 << 5 | 1 << 2, // ShouldWrap
};
/**
* !#en Specifies how time is treated when it is outside of the keyframe range of an Animation.
* !#zh 动画使用的循环模式。
* @enum WrapMode
* @memberof cc
*/
var WrapMode = cc.Enum({
/**
* !#en Reads the default wrap mode set higher up.
* !#zh 向 Animation Component 或者 AnimationClip 查找 wrapMode
* @property {Number} Default
*/
Default: 0,
/**
* !#en All iterations are played as specified.
* !#zh 动画只播放一遍
* @property {Number} Normal
*/
Normal: 1,
/**
* !#en All iterations are played in the reverse direction from the way they are specified.
* !#zh 从最后一帧或结束位置开始反向播放,到第一帧或开始位置停止
* @property {Number} Reverse
*/
Reverse: WrapModeMask.Reverse,
/**
* !#en When time reaches the end of the animation, time will continue at the beginning.
* !#zh 循环播放
* @property {Number} Loop
*/
Loop: WrapModeMask.Loop,
/**
* !#en All iterations are played in the reverse direction from the way they are specified.
* And when time reaches the start of the animation, time will continue at the ending.
* !#zh 反向循环播放
* @property {Number} LoopReverse
*/
LoopReverse: WrapModeMask.Loop | WrapModeMask.Reverse,
/**
* !#en Even iterations are played as specified, odd iterations are played in the reverse direction from the way they
* are specified.
* !#zh 从第一帧播放到最后一帧,然后反向播放回第一帧,到第一帧后再正向播放,如此循环
* @property {Number} PingPong
*/
PingPong: WrapModeMask.PingPong,
/**
* !#en Even iterations are played in the reverse direction from the way they are specified, odd iterations are played
* as specified.
* !#zh 从最后一帧开始反向播放,其他同 PingPong
* @property {Number} PingPongReverse
*/
PingPongReverse: WrapModeMask.PingPong | WrapModeMask.Reverse
});
cc.WrapMode = WrapMode;
// For internal
function WrappedInfo (info) {
if (info) {
this.set(info);
return;
}
this.ratio = 0;
this.time = 0;
this.direction = 1;
this.stopped = true;
this.iterations = 0;
this.frameIndex = undefined;
}
WrappedInfo.prototype.set = function (info) {
this.ratio = info.ratio;
this.time = info.time;
this.direction = info.direction;
this.stopped = info.stopped;
this.iterations = info.iterations;
this.frameIndex = info.frameIndex;
};
module.exports = {
WrapModeMask,
WrapMode,
WrappedInfo
};

View File

@@ -0,0 +1,568 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const EventTarget = require('../core/event/event-target');
const sys = require('../core/platform/CCSys');
const LoadMode = require('../core/assets/CCAudioClip').LoadMode;
let touchBinded = false;
let touchPlayList = [
//{ instance: Audio, offset: 0, audio: audio }
];
let Audio = function (src) {
EventTarget.call(this);
this._shouldRecycleOnEnded = false;
this._src = src;
this._element = null;
this.id = 0;
this._state = Audio.State.INITIALZING;
const self = this;
this._onended = function () {
self._state = Audio.State.STOPPED;
self.emit('ended');
};
this._onendedSecond = function () {
self._unbindEnded(self._onendedSecond);
self._bindEnded();
};
};
cc.js.extend(Audio, EventTarget);
/**
* !#en Audio state.
* !#zh 声音播放状态
* @enum audioEngine.AudioState
* @memberof cc
*/
// TODO - At present, the state is mixed with two states of users and systems, and it is best to split into two types. A "loading" should also be added to the system state.
Audio.State = {
/**
* @property {Number} ERROR
*/
ERROR: -1,
/**
* @property {Number} INITIALZING
*/
INITIALZING: 0,
/**
* @property {Number} PLAYING
*/
PLAYING: 1,
/**
* @property {Number} PAUSED
*/
PAUSED: 2,
/**
* @property {Number} STOPPED
*/
STOPPED: 3,
};
(function (proto) {
proto._bindEnded = function (callback) {
callback = callback || this._onended;
if (callback._binded) {
return;
}
callback._binded = true;
let elem = this._element;
if (this._src && (elem instanceof HTMLAudioElement)) {
elem.addEventListener('ended', callback);
} else {
elem.onended = callback;
}
};
proto._unbindEnded = function (callback) {
callback = callback || this._onended;
if (!callback._binded) {
return;
}
callback._binded = false;
let elem = this._element;
if (elem instanceof HTMLAudioElement) {
elem.removeEventListener('ended', callback);
} else if (elem) {
elem.onended = null;
}
};
proto._onLoaded = function () {
this._createElement();
this._state = Audio.State.INITIALZING;
this.setVolume(1);
this.setLoop(false);
};
proto._createElement = function () {
let elem = this._src._nativeAsset;
if (elem instanceof HTMLAudioElement) {
// Reuse dom audio element
if (!this._element) {
this._element = document.createElement('audio');
}
this._element.src = elem.src;
}
else {
this._element = new WebAudioElement(elem, this);
}
};
proto.play = function () {
let self = this;
this._src && this._src._ensureLoaded(function () {
// marked as playing so it will playOnLoad
self._state = Audio.State.PLAYING;
// TODO: move to audio event listeners
self._bindEnded();
let playPromise = self._element.play();
// dom audio throws an error if pause audio immediately after playing
if (window.Promise && playPromise instanceof Promise) {
playPromise.catch(function (err) {
// do nothing
});
}
self._touchToPlay();
});
};
proto._touchToPlay = function () {
if (this._src && this._src.loadMode === LoadMode.DOM_AUDIO &&
this._element.paused) {
touchPlayList.push({ instance: this, offset: 0, audio: this._element });
}
if (touchBinded) return;
touchBinded = true;
let touchEventName = ('ontouchend' in window) ? 'touchend' : 'mousedown';
// Listen to the touchstart body event and play the audio when necessary.
cc.game.canvas.addEventListener(touchEventName, function () {
let item;
while (item = touchPlayList.pop()) {
item.audio.play(item.offset);
}
});
};
proto.destroy = function () {
this._element = null;
};
proto.pause = function () {
if (this.getState() !== Audio.State.PLAYING) {
return;
}
let self = this;
this._src && this._src._ensureLoaded(function () {
// pause operation may fire 'ended' event
self._unbindEnded();
self._element.pause();
self._state = Audio.State.PAUSED;
});
};
proto.resume = function () {
if (this.getState() !== Audio.State.PAUSED) {
return;
}
let self = this;
this._src && this._src._ensureLoaded(function () {
self._bindEnded();
self._element.play();
self._state = Audio.State.PLAYING;
});
};
proto.stop = function () {
let self = this;
this._src && this._src._ensureLoaded(function () {
self._element.pause();
self._element.currentTime = 0;
// remove touchPlayList
for (let i = 0; i < touchPlayList.length; i++) {
if (touchPlayList[i].instance === self) {
touchPlayList.splice(i, 1);
break;
}
}
self._unbindEnded();
self.emit('stop');
self._state = Audio.State.STOPPED;
});
};
proto.setLoop = function (loop) {
let self = this;
this._src && this._src._ensureLoaded(function () {
self._element.loop = loop;
});
};
proto.getLoop = function () {
return this._element ? this._element.loop : false;
};
proto.setVolume = function (num) {
let self = this;
this._src && this._src._ensureLoaded(function () {
self._element.volume = num;
});
};
proto.getVolume = function () {
return this._element ? this._element.volume : 1;
};
proto.setCurrentTime = function (num) {
let self = this;
this._src && this._src._ensureLoaded(function () {
// setCurrentTime would fire 'ended' event
// so we need to change the callback to rebind ended callback after setCurrentTime
self._unbindEnded();
self._bindEnded(self._onendedSecond);
self._element.currentTime = num;
});
};
proto.getCurrentTime = function () {
return this._element ? this._element.currentTime : 0;
};
proto.getDuration = function () {
return this._src ? this._src.duration : 0;
};
proto.getState = function (forceUpdating = true) {
// HACK: in some browser, audio may not fire 'ended' event
// so we need to force updating the Audio state
if (forceUpdating) {
this._forceUpdatingState();
}
return this._state;
};
proto._forceUpdatingState = function () {
let elem = this._element;
if (elem) {
if (Audio.State.PLAYING === this._state && elem.paused) {
this._state = Audio.State.STOPPED;
}
else if (Audio.State.STOPPED === this._state && !elem.paused) {
this._state = Audio.State.PLAYING;
}
}
};
Object.defineProperty(proto, 'src', {
get: function () {
return this._src;
},
set: function (clip) {
this._unbindEnded();
if (clip && clip.isValid) {
if (clip !== this._src) {
this._src = clip;
if (!clip.loaded) {
let self = this;
// need to call clip._ensureLoaded mannually to start loading
clip.once('load', function () {
// In case set a new src when the old one hasn't finished loading
if (clip === self._src) {
self._onLoaded();
}
});
}
else {
this._onLoaded();
}
}
}
else {
this._src = null;
if (this._element instanceof WebAudioElement) {
this._element = null;
}
else if (this._element) {
this._element.src = '';
}
this._state = Audio.State.INITIALZING;
}
return clip;
},
enumerable: true,
configurable: true
});
Object.defineProperty(proto, 'paused', {
get: function () {
return this._element ? this._element.paused : true;
},
enumerable: true,
configurable: true
});
// setFinishCallback
})(Audio.prototype);
// TIME_CONSTANT is used as an argument of setTargetAtTime interface
// TIME_CONSTANT need to be a positive number on Edge and Baidu browser
// TIME_CONSTANT need to be 0 by default, or may fail to set volume at the very beginning of playing audio
let TIME_CONSTANT;
if (cc.sys.browserType === cc.sys.BROWSER_TYPE_EDGE ||
cc.sys.browserType === cc.sys.BROWSER_TYPE_BAIDU ||
cc.sys.browserType === cc.sys.BROWSER_TYPE_UC) {
TIME_CONSTANT = 0.01;
}
else {
TIME_CONSTANT = 0;
}
// Encapsulated WebAudio interface
let WebAudioElement = function (buffer, audio) {
this._audio = audio;
this._context = sys.__audioSupport.context;
this._buffer = buffer;
this._gainObj = this._context['createGain']();
this.volume = 1;
this._gainObj['connect'](this._context['destination']);
this._loop = false;
// The time stamp on the audio time axis when the recording begins to play.
this._startTime = -1;
// Record the currently playing 'Source'
this._currentSource = null;
// Record the time has been played
this.playedLength = 0;
this._currentTimer = null;
this._endCallback = function () {
if (this.onended) {
this.onended(this);
}
}.bind(this);
};
(function (proto) {
proto.play = function (offset) {
// If repeat play, you need to stop before an audio
if (this._currentSource && !this.paused) {
this._currentSource.onended = null;
this._currentSource.stop(0);
this.playedLength = 0;
}
let audio = this._context["createBufferSource"]();
audio.buffer = this._buffer;
audio["connect"](this._gainObj);
audio.loop = this._loop;
this._startTime = this._context.currentTime;
offset = offset || this.playedLength;
if (offset) {
this._startTime -= offset;
}
let duration = this._buffer.duration;
let startTime = offset;
let endTime;
if (this._loop) {
if (audio.start)
audio.start(0, startTime);
else if (audio["notoGrainOn"])
audio["noteGrainOn"](0, startTime);
else
audio["noteOn"](0, startTime);
} else {
endTime = duration - offset;
if (audio.start)
audio.start(0, startTime, endTime);
else if (audio["noteGrainOn"])
audio["noteGrainOn"](0, startTime, endTime);
else
audio["noteOn"](0, startTime, endTime);
}
this._currentSource = audio;
audio.onended = this._endCallback;
// If the current audio context time stamp is 0 and audio context state is suspended
// There may be a need to touch events before you can actually start playing audio
if ((!audio.context.state || audio.context.state === "suspended") && this._context.currentTime === 0) {
let self = this;
clearTimeout(this._currentTimer);
this._currentTimer = setTimeout(function () {
if (self._context.currentTime === 0) {
touchPlayList.push({
instance: self._audio,
offset: offset,
audio: self
});
}
}, 10);
}
let sys = cc.sys;
if (sys.os === sys.OS_IOS && sys.isBrowser && sys.isMobile) {
// Audio context is suspended when you unplug the earphones,
// and is interrupted when the app enters background.
// Both make the audioBufferSource unplayable.
if ((audio.context.state === "suspended" && this._context.currentTime !== 0)
|| audio.context.state === 'interrupted') {
// reference: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/resume
audio.context.resume();
}
}
};
proto.pause = function () {
clearTimeout(this._currentTimer);
if (this.paused) return;
// Record the time the current has been played
this.playedLength = this._context.currentTime - this._startTime;
// If more than the duration of the audio, Need to take the remainder
this.playedLength %= this._buffer.duration;
let audio = this._currentSource;
if (audio) {
if(audio.onended){
audio.onended._binded = false;
audio.onended = null;
}
audio.stop(0);
}
this._currentSource = null;
this._startTime = -1;
};
Object.defineProperty(proto, 'paused', {
get: function () {
// If the current audio is a loop, paused is false
if (this._currentSource && this._currentSource.loop)
return false;
// startTime default is -1
if (this._startTime === -1)
return true;
// Current time - Start playing time > Audio duration
return this._context.currentTime - this._startTime > this._buffer.duration;
},
enumerable: true,
configurable: true
});
Object.defineProperty(proto, 'loop', {
get: function () {
return this._loop;
},
set: function (bool) {
if (this._currentSource)
this._currentSource.loop = bool;
return this._loop = bool;
},
enumerable: true,
configurable: true
});
Object.defineProperty(proto, 'volume', {
get: function () {
return this._volume;
},
set: function (num) {
this._volume = num;
// https://www.chromestatus.com/features/5287995770929152
if (this._gainObj.gain.setTargetAtTime) {
try {
this._gainObj.gain.setTargetAtTime(num, this._context.currentTime, TIME_CONSTANT);
}
catch (e) {
// Some other unknown browsers may crash if TIME_CONSTANT is 0
this._gainObj.gain.setTargetAtTime(num, this._context.currentTime, 0.01);
}
}
else {
this._gainObj.gain.value = num;
}
if (sys.os === sys.OS_IOS && !this.paused && this._currentSource) {
// IOS must be stop webAudio
this._currentSource.onended = null;
this.pause();
this.play();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(proto, 'currentTime', {
get: function () {
if (this.paused) {
return this.playedLength;
}
// Record the time the current has been played
this.playedLength = this._context.currentTime - this._startTime;
// If more than the duration of the audio, Need to take the remainder
this.playedLength %= this._buffer.duration;
return this.playedLength;
},
set: function (num) {
if (!this.paused) {
this.pause();
this.playedLength = num;
this.play();
} else {
this.playedLength = num;
}
return num;
},
enumerable: true,
configurable: true
});
Object.defineProperty(proto, 'duration', {
get: function () {
return this._buffer.duration;
},
enumerable: true,
configurable: true
});
})(WebAudioElement.prototype);
module.exports = cc._Audio = Audio;

View File

@@ -0,0 +1,791 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const Audio = require('./CCAudio');
const AudioClip = require('../core/assets/CCAudioClip');
const js = cc.js;
let _instanceId = 0;
let _id2audio = js.createMap(true);
let _url2id = {};
let _audioPool = [];
let recycleAudio = function (audio) {
// In case repeatly recycle audio when users call audio.stop when audio finish playing
if (!audio._shouldRecycleOnEnded) {
return;
}
audio._finishCallback = null;
audio.off('ended');
audio.off('stop');
audio.src = null;
// In case repeatly recycle audio
if (!_audioPool.includes(audio)) {
if (_audioPool.length < audioEngine._maxPoolSize) {
_audioPool.push(audio);
}
else {
audio.destroy();
}
}
audio._shouldRecycleOnEnded = false;
};
let getAudioFromPath = function (path) {
var id = ++_instanceId;
var list = _url2id[path];
if (!list) {
list = _url2id[path] = [];
}
if (audioEngine._maxAudioInstance <= list.length) {
var oldId = list.shift();
var oldAudio = getAudioFromId(oldId);
// Stop will recycle audio automatically by event callback
oldAudio.stop();
}
var audio = _audioPool.pop() || new Audio();
var callback = function () {
var audioInList = getAudioFromId(this.id);
if (audioInList) {
delete _id2audio[this.id];
var index = list.indexOf(this.id);
cc.js.array.fastRemoveAt(list, index);
}
recycleAudio(this);
};
audio.on('ended', function () {
if (this._finishCallback) {
this._finishCallback();
}
if(!this.getLoop()){
callback.call(this);
}
}, audio);
audio.on('stop', callback, audio);
audio.id = id;
_id2audio[id] = audio;
list.push(id);
return audio;
};
let getAudioFromId = function (id) {
return _id2audio[id];
};
let handleVolume = function (volume) {
if (volume === undefined) {
// set default volume as 1
volume = 1;
}
else if (typeof volume === 'string') {
volume = Number.parseFloat(volume);
}
return volume;
};
/**
* !#en `cc.audioEngine` is the singleton object, it provide simple audio APIs.
* !#zh
* cc.audioengine是单例对象。<br/>
* 主要用来播放音频,播放的时候会返回一个 audioID之后都可以通过这个 audioID 来操作这个音频对象。<br/>
* 不使用的时候,请使用 `cc.audioEngine.uncache(filePath);` 进行资源释放 <br/>
* 注意:<br/>
* 在 Android 系统浏览器上,不同浏览器,不同版本的效果不尽相同。<br/>
* 比如说:大多数浏览器都需要用户物理交互才可以开始播放音效,有一些不支持 WebAudio有一些不支持多音轨播放。总之如果对音乐依赖比较强请做尽可能多的测试。
* @class audioEngine
* @static
*/
var audioEngine = {
AudioState: Audio.State,
_maxAudioInstance: 24,
_maxPoolSize: 32,
_id2audio: _id2audio,
/**
* !#en Play audio.
* !#zh 播放音频
* @method play
* @param {AudioClip} clip - The audio clip to play.
* @param {Boolean} loop - Whether the music loop or not.
* @param {Number} volume - Volume size.
* @return {Number} audioId
* @example
* cc.resources.load(path, cc.AudioClip, null, function (err, clip) {
* var audioID = cc.audioEngine.play(clip, false, 0.5);
* });
*/
play: function (clip, loop, volume) {
if (CC_EDITOR) {
return;
}
if (!(clip instanceof AudioClip)) {
return cc.error('Wrong type of AudioClip.');
}
let path = clip.nativeUrl;
let audio = getAudioFromPath(path);
audio.src = clip;
clip._ensureLoaded();
audio._shouldRecycleOnEnded = true;
audio.setLoop(loop || false);
volume = handleVolume(volume);
audio.setVolume(volume);
audio.play();
return audio.id;
},
/**
* !#en Set audio loop.
* !#zh 设置音频是否循环。
* @method setLoop
* @param {Number} audioID - audio id.
* @param {Boolean} loop - Whether cycle.
* @example
* cc.audioEngine.setLoop(id, true);
*/
setLoop: function (audioID, loop) {
var audio = getAudioFromId(audioID);
if (!audio || !audio.setLoop)
return;
audio.setLoop(loop);
},
/**
* !#en Get audio cycle state.
* !#zh 获取音频的循环状态。
* @method isLoop
* @param {Number} audioID - audio id.
* @return {Boolean} Whether cycle.
* @example
* cc.audioEngine.isLoop(id);
*/
isLoop: function (audioID) {
var audio = getAudioFromId(audioID);
if (!audio || !audio.getLoop)
return false;
return audio.getLoop();
},
/**
* !#en Set the volume of audio.
* !#zh 设置音量0.0 ~ 1.0)。
* @method setVolume
* @param {Number} audioID - audio id.
* @param {Number} volume - Volume must be in 0.0~1.0 .
* @example
* cc.audioEngine.setVolume(id, 0.5);
*/
setVolume: function (audioID, volume) {
var audio = getAudioFromId(audioID);
if (audio) {
audio.setVolume(volume);
}
},
/**
* !#en The volume of the music max value is 1.0,the min value is 0.0 .
* !#zh 获取音量0.0 ~ 1.0)。
* @method getVolume
* @param {Number} audioID - audio id.
* @return {Number}
* @example
* var volume = cc.audioEngine.getVolume(id);
*/
getVolume: function (audioID) {
var audio = getAudioFromId(audioID);
return audio ? audio.getVolume() : 1;
},
/**
* !#en Set current time
* !#zh 设置当前的音频时间。
* @method setCurrentTime
* @param {Number} audioID - audio id.
* @param {Number} sec - current time.
* @return {Boolean}
* @example
* cc.audioEngine.setCurrentTime(id, 2);
*/
setCurrentTime: function (audioID, sec) {
var audio = getAudioFromId(audioID);
if (audio) {
audio.setCurrentTime(sec);
return true;
}
else {
return false;
}
},
/**
* !#en Get current time
* !#zh 获取当前的音频播放时间。
* @method getCurrentTime
* @param {Number} audioID - audio id.
* @return {Number} audio current time.
* @example
* var time = cc.audioEngine.getCurrentTime(id);
*/
getCurrentTime: function (audioID) {
var audio = getAudioFromId(audioID);
return audio ? audio.getCurrentTime() : 0;
},
/**
* !#en Get audio duration
* !#zh 获取音频总时长。
* @method getDuration
* @param {Number} audioID - audio id.
* @return {Number} audio duration.
* @example
* var time = cc.audioEngine.getDuration(id);
*/
getDuration: function (audioID) {
var audio = getAudioFromId(audioID);
return audio ? audio.getDuration() : 0;
},
/**
* !#en Get audio state
* !#zh 获取音频状态。
* @method getState
* @param {Number} audioID - audio id.
* @return {audioEngine.AudioState} audio duration.
* @example
* var state = cc.audioEngine.getState(id);
*/
getState: function (audioID) {
var audio = getAudioFromId(audioID);
return audio ? audio.getState() : this.AudioState.ERROR;
},
/**
* !#en Whether the audio is playing
* !#zh 音乐是否正在播放
* @method isPlaying
* @return {Boolean}
* @example
* cc.audioEngine.isPlaying(audioID);
*/
isPlaying: function(audioID) {
return this.getState(audioID) === this.AudioState.PLAYING;
},
/**
* !#en Set Audio finish callback
* !#zh 设置一个音频结束后的回调
* @method setFinishCallback
* @param {Number} audioID - audio id.
* @param {Function} callback - loaded callback.
* @example
* cc.audioEngine.setFinishCallback(id, function () {});
*/
setFinishCallback: function (audioID, callback) {
var audio = getAudioFromId(audioID);
if (!audio)
return;
audio._finishCallback = callback;
},
/**
* !#en Pause playing audio.
* !#zh 暂停正在播放音频。
* @method pause
* @param {Number} audioID - The return value of function play.
* @example
* cc.audioEngine.pause(audioID);
*/
pause: function (audioID) {
var audio = getAudioFromId(audioID);
if (audio) {
audio.pause();
return true;
}
else {
return false;
}
},
_pauseIDCache: [],
/**
* !#en Pause all playing audio
* !#zh 暂停现在正在播放的所有音频。
* @method pauseAll
* @example
* cc.audioEngine.pauseAll();
*/
pauseAll: function () {
for (var id in _id2audio) {
var audio = _id2audio[id];
var state = audio.getState();
if (state === Audio.State.PLAYING) {
this._pauseIDCache.push(id);
audio.pause();
}
}
},
/**
* !#en Resume playing audio.
* !#zh 恢复播放指定的音频。
* @method resume
* @param {Number} audioID - The return value of function play.
* @example
* cc.audioEngine.resume(audioID);
*/
resume: function (audioID) {
var audio = getAudioFromId(audioID);
if (audio) {
audio.resume();
}
},
/**
* !#en Resume all playing audio.
* !#zh 恢复播放所有之前暂停的所有音频。
* @method resumeAll
* @example
* cc.audioEngine.resumeAll();
*/
resumeAll: function () {
for (var i = 0; i < this._pauseIDCache.length; ++i) {
var id = this._pauseIDCache[i];
var audio = getAudioFromId(id);
if (audio)
audio.resume();
}
this._pauseIDCache.length = 0;
},
/**
* !#en Stop playing audio.
* !#zh 停止播放指定音频。
* @method stop
* @param {Number} audioID - The return value of function play.
* @example
* cc.audioEngine.stop(audioID);
*/
stop: function (audioID) {
var audio = getAudioFromId(audioID);
if (audio) {
// Stop will recycle audio automatically by event callback
audio.stop();
return true;
}
else {
return false;
}
},
/**
* !#en Stop all playing audio.
* !#zh 停止正在播放的所有音频。
* @method stopAll
* @example
* cc.audioEngine.stopAll();
*/
stopAll: function () {
for (var id in _id2audio) {
var audio = _id2audio[id];
if (audio) {
// Stop will recycle audio automatically by event callback
audio.stop();
}
}
},
/**
* !#en Set up an audio can generate a few examples.
* !#zh 设置一个音频可以设置几个实例
* @method setMaxAudioInstance
* @param {Number} num - a number of instances to be created from within an audio
* @example
* cc.audioEngine.setMaxAudioInstance(20);
* @deprecated since v2.4.0
*/
setMaxAudioInstance: function (num) {
if (CC_DEBUG) {
cc.warn('Since v2.4.0, maxAudioInstance has become a read only property.\n'
+ 'audioEngine.setMaxAudioInstance() method will be removed in the future');
}
},
/**
* !#en Getting audio can produce several examples.
* !#zh 获取一个音频可以设置几个实例
* @method getMaxAudioInstance
* @return {Number} max number of instances to be created from within an audio
* @example
* cc.audioEngine.getMaxAudioInstance();
*/
getMaxAudioInstance: function () {
return this._maxAudioInstance;
},
/**
* !#en Unload the preloaded audio from internal buffer.
* !#zh 卸载预加载的音频。
* @method uncache
* @param {AudioClip} clip
* @example
* cc.audioEngine.uncache(filePath);
*/
uncache: function (clip) {
var filePath = clip;
if (typeof clip === 'string') {
// backward compatibility since 1.10
cc.warnID(8401, 'cc.audioEngine', 'cc.AudioClip', 'AudioClip', 'cc.AudioClip', 'audio');
filePath = clip;
}
else {
if (!clip) {
return;
}
filePath = clip.nativeUrl;
}
var list = _url2id[filePath];
if (!list) return;
while (list.length > 0) {
var id = list.pop();
var audio = _id2audio[id];
if (audio) {
// Stop will recycle audio automatically by event callback
audio.stop();
delete _id2audio[id];
}
}
},
/**
* !#en Unload all audio from internal buffer.
* !#zh 卸载所有音频。
* @method uncacheAll
* @example
* cc.audioEngine.uncacheAll();
*/
uncacheAll: function () {
this.stopAll();
let audio;
for (let id in _id2audio) {
audio = _id2audio[id];
if (audio) {
audio.destroy();
}
}
while (audio = _audioPool.pop()) {
audio.destroy();
}
_id2audio = js.createMap(true);
_url2id = {};
},
_breakCache: null,
_break: function () {
this._breakCache = [];
for (var id in _id2audio) {
var audio = _id2audio[id];
var state = audio.getState();
if (state === Audio.State.PLAYING) {
this._breakCache.push(id);
audio.pause();
}
}
},
_restore: function () {
if (!this._breakCache) return;
while (this._breakCache.length > 0) {
var id = this._breakCache.pop();
var audio = getAudioFromId(id);
if (audio && audio.resume)
audio.resume();
}
this._breakCache = null;
},
///////////////////////////////
// Classification of interface
_music: {
id: -1,
loop: false,
volume: 1,
},
_effect: {
volume: 1,
pauseCache: [],
},
/**
* !#en Play background music
* !#zh 播放背景音乐
* @method playMusic
* @param {AudioClip} clip - The audio clip to play.
* @param {Boolean} loop - Whether the music loop or not.
* @return {Number} audioId
* @example
* cc.resources.load(path, cc.AudioClip, null, function (err, clip) {
* var audioID = cc.audioEngine.playMusic(clip, false);
* });
*/
playMusic: function (clip, loop) {
var music = this._music;
this.stop(music.id);
music.id = this.play(clip, loop, music.volume);
music.loop = loop;
return music.id;
},
/**
* !#en Stop background music.
* !#zh 停止播放背景音乐。
* @method stopMusic
* @example
* cc.audioEngine.stopMusic();
*/
stopMusic: function () {
this.stop(this._music.id);
},
/**
* !#en Pause the background music.
* !#zh 暂停播放背景音乐。
* @method pauseMusic
* @example
* cc.audioEngine.pauseMusic();
*/
pauseMusic: function () {
this.pause(this._music.id);
return this._music.id;
},
/**
* !#en Resume playing background music.
* !#zh 恢复播放背景音乐。
* @method resumeMusic
* @example
* cc.audioEngine.resumeMusic();
*/
resumeMusic: function () {
this.resume(this._music.id);
return this._music.id;
},
/**
* !#en Get the volume(0.0 ~ 1.0).
* !#zh 获取音量0.0 ~ 1.0)。
* @method getMusicVolume
* @return {Number}
* @example
* var volume = cc.audioEngine.getMusicVolume();
*/
getMusicVolume: function () {
return this._music.volume;
},
/**
* !#en Set the background music volume.
* !#zh 设置背景音乐音量0.0 ~ 1.0)。
* @method setMusicVolume
* @param {Number} volume - Volume must be in 0.0~1.0.
* @example
* cc.audioEngine.setMusicVolume(0.5);
*/
setMusicVolume: function (volume) {
volume = handleVolume(volume);
var music = this._music;
music.volume = volume;
this.setVolume(music.id, music.volume);
return music.volume;
},
/**
* !#en Background music playing state
* !#zh 背景音乐是否正在播放
* @method isMusicPlaying
* @return {Boolean}
* @example
* cc.audioEngine.isMusicPlaying();
*/
isMusicPlaying: function () {
return this.getState(this._music.id) === this.AudioState.PLAYING;
},
/**
* !#en Play effect audio.
* !#zh 播放音效
* @method playEffect
* @param {AudioClip} clip - The audio clip to play.
* @param {Boolean} loop - Whether the music loop or not.
* @return {Number} audioId
* @example
* cc.resources.load(path, cc.AudioClip, null, function (err, clip) {
* var audioID = cc.audioEngine.playEffect(clip, false);
* });
*/
playEffect: function (clip, loop) {
return this.play(clip, loop || false, this._effect.volume);
},
/**
* !#en Set the volume of effect audio.
* !#zh 设置音效音量0.0 ~ 1.0)。
* @method setEffectsVolume
* @param {Number} volume - Volume must be in 0.0~1.0.
* @example
* cc.audioEngine.setEffectsVolume(0.5);
*/
setEffectsVolume: function (volume) {
volume = handleVolume(volume);
var musicId = this._music.id;
this._effect.volume = volume;
for (var id in _id2audio) {
var audio = _id2audio[id];
if (!audio || audio.id === musicId) continue;
audioEngine.setVolume(id, volume);
}
},
/**
* !#en The volume of the effect audio max value is 1.0,the min value is 0.0 .
* !#zh 获取音效音量0.0 ~ 1.0)。
* @method getEffectsVolume
* @return {Number}
* @example
* var volume = cc.audioEngine.getEffectsVolume();
*/
getEffectsVolume: function () {
return this._effect.volume;
},
/**
* !#en Pause effect audio.
* !#zh 暂停播放音效。
* @method pauseEffect
* @param {Number} audioID - audio id.
* @example
* cc.audioEngine.pauseEffect(audioID);
*/
pauseEffect: function (audioID) {
return this.pause(audioID);
},
/**
* !#en Stop playing all the sound effects.
* !#zh 暂停播放所有音效。
* @method pauseAllEffects
* @example
* cc.audioEngine.pauseAllEffects();
*/
pauseAllEffects: function () {
var musicId = this._music.id;
var effect = this._effect;
effect.pauseCache.length = 0;
for (var id in _id2audio) {
var audio = _id2audio[id];
if (!audio || audio.id === musicId) continue;
var state = audio.getState();
if (state === this.AudioState.PLAYING) {
effect.pauseCache.push(id);
audio.pause();
}
}
},
/**
* !#en Resume effect audio.
* !#zh 恢复播放音效音频。
* @method resumeEffect
* @param {Number} audioID - The return value of function play.
* @example
* cc.audioEngine.resumeEffect(audioID);
*/
resumeEffect: function (id) {
this.resume(id);
},
/**
* !#en Resume all effect audio.
* !#zh 恢复播放所有之前暂停的音效。
* @method resumeAllEffects
* @example
* cc.audioEngine.resumeAllEffects();
*/
resumeAllEffects: function () {
var pauseIDCache = this._effect.pauseCache;
for (var i = 0; i < pauseIDCache.length; ++i) {
var id = pauseIDCache[i];
var audio = _id2audio[id];
if (audio)
audio.resume();
}
},
/**
* !#en Stop playing the effect audio.
* !#zh 停止播放音效。
* @method stopEffect
* @param {Number} audioID - audio id.
* @example
* cc.audioEngine.stopEffect(id);
*/
stopEffect: function (audioID) {
return this.stop(audioID);
},
/**
* !#en Stop playing all the effects.
* !#zh 停止播放所有音效。
* @method stopAllEffects
* @example
* cc.audioEngine.stopAllEffects();
*/
stopAllEffects: function () {
var musicId = this._music.id;
for (var id in _id2audio) {
var audio = _id2audio[id];
if (!audio || audio.id === musicId) continue;
var state = audio.getState();
if (state === audioEngine.AudioState.PLAYING) {
audio.stop();
}
}
}
};
module.exports = cc.audioEngine = audioEngine;

View File

@@ -0,0 +1,77 @@
/*--
Copyright 2009-2010 by Stefan Rusterholz.
All rights reserved.
You can choose between MIT and BSD-3-Clause license. License file will be added later.
--*/
var codec = {name:'Jacob__Codec'};
codec.Base64 = require('./base64');
codec.GZip = require('./gzip');
/**
* Unpack a gzipped byte array
* @param {Array} input Byte array
* @returns {String} Unpacked byte string
*/
codec.unzip = function () {
return codec.GZip.gunzip.apply(codec.GZip, arguments);
};
/**
* Unpack a gzipped byte string encoded as base64
* @param {String} input Byte string encoded as base64
* @returns {String} Unpacked byte string
*/
codec.unzipBase64 = function () {
var buffer = codec.Base64.decode.apply(codec.Base64, arguments);
try {
return codec.GZip.gunzip.call(codec.GZip, buffer);
}
catch(e) {
// if not zipped, just skip
return buffer.slice(7); // get image data
}
};
/**
* Unpack a gzipped byte string encoded as base64
* @param {String} input Byte string encoded as base64
* @param {Number} bytes Bytes per array item
* @returns {Array} Unpacked byte array
*/
codec.unzipBase64AsArray = function (input, bytes) {
bytes = bytes || 1;
var dec = this.unzipBase64(input),
ar = [], i, j, len;
for (i = 0, len = dec.length / bytes; i < len; i++) {
ar[i] = 0;
for (j = bytes - 1; j >= 0; --j) {
ar[i] += dec.charCodeAt((i * bytes) + j) << (j * 8);
}
}
return ar;
};
/**
* Unpack a gzipped byte array
* @param {Array} input Byte array
* @param {Number} bytes Bytes per array item
* @returns {Array} Unpacked byte array
*/
codec.unzipAsArray = function (input, bytes) {
bytes = bytes || 1;
var dec = this.unzip(input),
ar = [], i, j, len;
for (i = 0, len = dec.length / bytes; i < len; i++) {
ar[i] = 0;
for (j = bytes - 1; j >= 0; --j) {
ar[i] += dec.charCodeAt((i * bytes) + j) << (j * 8);
}
}
return ar;
};
cc.codec = module.exports = codec;

View File

@@ -0,0 +1,85 @@
/*--
Copyright 2009-2010 by Stefan Rusterholz.
All rights reserved.
You can choose between MIT and BSD-3-Clause license. License file will be added later.
--*/
var misc = require('../core/utils/misc');
var strValue = misc.BASE64_VALUES;
/**
* mixin cc.Codec.Base64
*/
var Base64 = {name:'Jacob__Codec__Base64'};
/**
* <p>
* cc.Codec.Base64.decode(input[, unicode=false]) -> String (http://en.wikipedia.org/wiki/Base64).
* </p>
* @function
* @param {String} input The base64 encoded string to decode
* @return {String} Decodes a base64 encoded String
* @example
* //decode string
* cc.Codec.Base64.decode("U29tZSBTdHJpbmc="); // => "Some String"
*/
Base64.decode = function Jacob__Codec__Base64__decode(input) {
var output = [],
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = strValue[input.charCodeAt(i++)];
enc2 = strValue[input.charCodeAt(i++)];
enc3 = strValue[input.charCodeAt(i++)];
enc4 = strValue[input.charCodeAt(i++)];
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(String.fromCharCode(chr1));
if (enc3 !== 64) {
output.push(String.fromCharCode(chr2));
}
if (enc4 !== 64) {
output.push(String.fromCharCode(chr3));
}
}
output = output.join('');
return output;
};
/**
* <p>
* Converts an input string encoded in base64 to an array of integers whose<br/>
* values represent the decoded string's characters' bytes.
* </p>
* @function
* @param {String} input The String to convert to an array of Integers
* @param {Number} bytes
* @return {Array}
* @example
* //decode string to array
* var decodeArr = cc.Codec.Base64.decodeAsArray("U29tZSBTdHJpbmc=");
*/
Base64.decodeAsArray = function Jacob__Codec__Base64___decodeAsArray(input, bytes) {
var dec = this.decode(input),
ar = [], i, j, len;
for (i = 0, len = dec.length / bytes; i < len; i++) {
ar[i] = 0;
for (j = bytes - 1; j >= 0; --j) {
ar[i] += dec.charCodeAt((i * bytes) + j) << (j * 8);
}
}
return ar;
};
module.exports = Base64;

View File

@@ -0,0 +1,733 @@
/*--
Copyright 2009-2010 by Stefan Rusterholz.
All rights reserved.
You can choose between MIT and BSD-3-Clause license. License file will be added later.
--*/
/**
* See cc.Codec.GZip.gunzip.
* @param {Array | String} data The bytestream to decompress
* Constructor
*/
var GZip = function Jacob__GZip(data) {
this.data = data;
this.debug = false;
this.gpflags = undefined;
this.files = 0;
this.unzipped = [];
this.buf32k = new Array(32768);
this.bIdx = 0;
this.modeZIP = false;
this.bytepos = 0;
this.bb = 1;
this.bits = 0;
this.nameBuf = [];
this.fileout = undefined;
this.literalTree = new Array(GZip.LITERALS);
this.distanceTree = new Array(32);
this.treepos = 0;
this.Places = null;
this.len = 0;
this.fpos = new Array(17);
this.fpos[0] = 0;
this.flens = undefined;
this.fmax = undefined;
};
/**
* Unzips the gzipped data of the 'data' argument.
* @param string The bytestream to decompress. Either an array of Integers between 0 and 255, or a String.
* @return {String}
*/
GZip.gunzip = function (string) {
if (string.constructor === Array) {
} else if (string.constructor === String) {
}
var gzip = new GZip(string);
return gzip.gunzip()[0][0];
};
GZip.HufNode = function () {
this.b0 = 0;
this.b1 = 0;
this.jump = null;
this.jumppos = -1;
};
/**
* @constant
* @type Number
*/
GZip.LITERALS = 288;
/**
* @constant
* @type Number
*/
GZip.NAMEMAX = 256;
GZip.bitReverse = [
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
];
GZip.cplens = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
];
GZip.cplext = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
];
/* 99==invalid */
GZip.cpdist = [
0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d,
0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1,
0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01,
0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001
];
GZip.cpdext = [
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13
];
GZip.border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
/**
* gunzip
* @return {Array}
*/
GZip.prototype.gunzip = function () {
this.outputArr = [];
//convertToByteArray(input);
//if (this.debug) alert(this.data);
this.nextFile();
return this.unzipped;
};
GZip.prototype.readByte = function () {
this.bits += 8;
if (this.bytepos < this.data.length) {
//return this.data[this.bytepos++]; // Array
return this.data.charCodeAt(this.bytepos++);
} else {
return -1;
}
};
GZip.prototype.byteAlign = function () {
this.bb = 1;
};
GZip.prototype.readBit = function () {
var carry;
this.bits++;
carry = (this.bb & 1);
this.bb >>= 1;
if (this.bb === 0) {
this.bb = this.readByte();
carry = (this.bb & 1);
this.bb = (this.bb >> 1) | 0x80;
}
return carry;
};
GZip.prototype.readBits = function (a) {
var res = 0,
i = a;
while (i--) res = (res << 1) | this.readBit();
if (a) res = GZip.bitReverse[res] >> (8 - a);
return res;
};
GZip.prototype.flushBuffer = function () {
this.bIdx = 0;
};
GZip.prototype.addBuffer = function (a) {
this.buf32k[this.bIdx++] = a;
this.outputArr.push(String.fromCharCode(a));
if (this.bIdx === 0x8000) this.bIdx = 0;
};
GZip.prototype.IsPat = function () {
while (1) {
if (this.fpos[this.len] >= this.fmax) return -1;
if (this.flens[this.fpos[this.len]] === this.len) return this.fpos[this.len]++;
this.fpos[this.len]++;
}
};
GZip.prototype.Rec = function () {
var curplace = this.Places[this.treepos];
var tmp;
//if (this.debug) document.write("<br>len:"+this.len+" treepos:"+this.treepos);
if (this.len === 17) { //war 17
return -1;
}
this.treepos++;
this.len++;
tmp = this.IsPat();
//if (this.debug) document.write("<br>IsPat "+tmp);
if (tmp >= 0) {
curplace.b0 = tmp;
/* leaf cell for 0-bit */
//if (this.debug) document.write("<br>b0 "+curplace.b0);
} else {
/* Not a Leaf cell */
curplace.b0 = 0x8000;
//if (this.debug) document.write("<br>b0 "+curplace.b0);
if (this.Rec()) return -1;
}
tmp = this.IsPat();
if (tmp >= 0) {
curplace.b1 = tmp;
/* leaf cell for 1-bit */
//if (this.debug) document.write("<br>b1 "+curplace.b1);
curplace.jump = null;
/* Just for the display routine */
} else {
/* Not a Leaf cell */
curplace.b1 = 0x8000;
//if (this.debug) document.write("<br>b1 "+curplace.b1);
curplace.jump = this.Places[this.treepos];
curplace.jumppos = this.treepos;
if (this.Rec()) return -1;
}
this.len--;
return 0;
};
GZip.prototype.CreateTree = function (currentTree, numval, lengths, show) {
var i;
/* Create the Huffman decode tree/table */
//if (this.debug) document.write("currentTree "+currentTree+" numval "+numval+" lengths "+lengths+" show "+show);
this.Places = currentTree;
this.treepos = 0;
this.flens = lengths;
this.fmax = numval;
for (i = 0; i < 17; i++) this.fpos[i] = 0;
this.len = 0;
if (this.Rec()) {
//if (this.debug) alert("invalid huffman tree\n");
return -1;
}
// if (this.debug) {
// document.write('<br>Tree: '+this.Places.length);
// for (var a=0;a<32;a++){
// document.write("Places["+a+"].b0="+this.Places[a].b0+"<br>");
// document.write("Places["+a+"].b1="+this.Places[a].b1+"<br>");
// }
// }
return 0;
};
GZip.prototype.DecodeValue = function (currentTree) {
var len, i,
xtreepos = 0,
X = currentTree[xtreepos],
b;
/* decode one symbol of the data */
while (1) {
b = this.readBit();
// if (this.debug) document.write("b="+b);
if (b) {
if (!(X.b1 & 0x8000)) {
// if (this.debug) document.write("ret1");
return X.b1;
/* If leaf node, return data */
}
X = X.jump;
len = currentTree.length;
for (i = 0; i < len; i++) {
if (currentTree[i] === X) {
xtreepos = i;
break;
}
}
} else {
if (!(X.b0 & 0x8000)) {
// if (this.debug) document.write("ret2");
return X.b0;
/* If leaf node, return data */
}
xtreepos++;
X = currentTree[xtreepos];
}
}
// if (this.debug) document.write("ret3");
return -1;
};
GZip.prototype.DeflateLoop = function () {
var last, c, type, i, len;
do {
last = this.readBit();
type = this.readBits(2);
if (type === 0) {
var blockLen, cSum;
// Stored
this.byteAlign();
blockLen = this.readByte();
blockLen |= (this.readByte() << 8);
cSum = this.readByte();
cSum |= (this.readByte() << 8);
if (((blockLen ^ ~cSum) & 0xffff)) {
document.write("BlockLen checksum mismatch\n"); // FIXME: use throw
}
while (blockLen--) {
c = this.readByte();
this.addBuffer(c);
}
} else if (type === 1) {
var j;
/* Fixed Huffman tables -- fixed decode routine */
while (1) {
/*
256 0000000 0
: : :
279 0010111 23
0 00110000 48
: : :
143 10111111 191
280 11000000 192
: : :
287 11000111 199
144 110010000 400
: : :
255 111111111 511
Note the bit order!
*/
j = (GZip.bitReverse[this.readBits(7)] >> 1);
if (j > 23) {
j = (j << 1) | this.readBit();
/* 48..255 */
if (j > 199) { /* 200..255 */
j -= 128;
/* 72..127 */
j = (j << 1) | this.readBit();
/* 144..255 << */
} else { /* 48..199 */
j -= 48;
/* 0..151 */
if (j > 143) {
j = j + 136;
/* 280..287 << */
/* 0..143 << */
}
}
} else { /* 0..23 */
j += 256;
/* 256..279 << */
}
if (j < 256) {
this.addBuffer(j);
} else if (j === 256) {
/* EOF */
break; // FIXME: make this the loop-condition
} else {
var len, dist;
j -= 256 + 1;
/* bytes + EOF */
len = this.readBits(GZip.cplext[j]) + GZip.cplens[j];
j = GZip.bitReverse[this.readBits(5)] >> 3;
if (GZip.cpdext[j] > 8) {
dist = this.readBits(8);
dist |= (this.readBits(GZip.cpdext[j] - 8) << 8);
} else {
dist = this.readBits(GZip.cpdext[j]);
}
dist += GZip.cpdist[j];
for (j = 0; j < len; j++) {
var c = this.buf32k[(this.bIdx - dist) & 0x7fff];
this.addBuffer(c);
}
}
} // while
} else if (type === 2) {
var j, n, literalCodes, distCodes, lenCodes;
var ll = new Array(288 + 32); // "static" just to preserve stack
// Dynamic Huffman tables
literalCodes = 257 + this.readBits(5);
distCodes = 1 + this.readBits(5);
lenCodes = 4 + this.readBits(4);
for (j = 0; j < 19; j++) {
ll[j] = 0;
}
// Get the decode tree code lengths
for (j = 0; j < lenCodes; j++) {
ll[GZip.border[j]] = this.readBits(3);
}
len = this.distanceTree.length;
for (i = 0; i < len; i++) this.distanceTree[i] = new GZip.HufNode();
if (this.CreateTree(this.distanceTree, 19, ll, 0)) {
this.flushBuffer();
return 1;
}
// if (this.debug) {
// document.write("<br>distanceTree");
// for(var a=0;a<this.distanceTree.length;a++){
// document.write("<br>"+this.distanceTree[a].b0+" "+this.distanceTree[a].b1+" "+this.distanceTree[a].jump+" "+this.distanceTree[a].jumppos);
// }
// }
//read in literal and distance code lengths
n = literalCodes + distCodes;
i = 0;
var z = -1;
// if (this.debug) document.write("<br>n="+n+" bits: "+this.bits+"<br>");
while (i < n) {
z++;
j = this.DecodeValue(this.distanceTree);
// if (this.debug) document.write("<br>"+z+" i:"+i+" decode: "+j+" bits "+this.bits+"<br>");
if (j < 16) { // length of code in bits (0..15)
ll[i++] = j;
} else if (j === 16) { // repeat last length 3 to 6 times
var l;
j = 3 + this.readBits(2);
if (i + j > n) {
this.flushBuffer();
return 1;
}
l = i ? ll[i - 1] : 0;
while (j--) {
ll[i++] = l;
}
} else {
if (j === 17) { // 3 to 10 zero length codes
j = 3 + this.readBits(3);
} else { // j == 18: 11 to 138 zero length codes
j = 11 + this.readBits(7);
}
if (i + j > n) {
this.flushBuffer();
return 1;
}
while (j--) {
ll[i++] = 0;
}
}
} // while
// Can overwrite tree decode tree as it is not used anymore
len = this.literalTree.length;
for (i = 0; i < len; i++)
this.literalTree[i] = new GZip.HufNode();
if (this.CreateTree(this.literalTree, literalCodes, ll, 0)) {
this.flushBuffer();
return 1;
}
len = this.literalTree.length;
for (i = 0; i < len; i++) this.distanceTree[i] = new GZip.HufNode();
var ll2 = new Array();
for (i = literalCodes; i < ll.length; i++) ll2[i - literalCodes] = ll[i];
if (this.CreateTree(this.distanceTree, distCodes, ll2, 0)) {
this.flushBuffer();
return 1;
}
// if (this.debug) document.write("<br>literalTree");
while (1) {
j = this.DecodeValue(this.literalTree);
if (j >= 256) { // In C64: if carry set
var len, dist;
j -= 256;
if (j === 0) {
// EOF
break;
}
j--;
len = this.readBits(GZip.cplext[j]) + GZip.cplens[j];
j = this.DecodeValue(this.distanceTree);
if (GZip.cpdext[j] > 8) {
dist = this.readBits(8);
dist |= (this.readBits(GZip.cpdext[j] - 8) << 8);
} else {
dist = this.readBits(GZip.cpdext[j]);
}
dist += GZip.cpdist[j];
while (len--) {
var c = this.buf32k[(this.bIdx - dist) & 0x7fff];
this.addBuffer(c);
}
} else {
this.addBuffer(j);
}
} // while
}
} while (!last);
this.flushBuffer();
this.byteAlign();
return 0;
};
GZip.prototype.unzipFile = function (name) {
var i;
this.gunzip();
for (i = 0; i < this.unzipped.length; i++) {
if (this.unzipped[i][1] === name) {
return this.unzipped[i][0];
}
}
};
GZip.prototype.nextFile = function () {
// if (this.debug) alert("NEXTFILE");
this.outputArr = [];
this.modeZIP = false;
var tmp = [];
tmp[0] = this.readByte();
tmp[1] = this.readByte();
// if (this.debug) alert("type: "+tmp[0]+" "+tmp[1]);
if (tmp[0] === 0x78 && tmp[1] === 0xda) { //GZIP
// if (this.debug) alert("GEONExT-GZIP");
this.DeflateLoop();
// if (this.debug) alert(this.outputArr.join(''));
this.unzipped[this.files] = [this.outputArr.join(''), "geonext.gxt"];
this.files++;
}
if (tmp[0] === 0x1f && tmp[1] === 0x8b) { //GZIP
// if (this.debug) alert("GZIP");
this.skipdir();
// if (this.debug) alert(this.outputArr.join(''));
this.unzipped[this.files] = [this.outputArr.join(''), "file"];
this.files++;
}
if (tmp[0] === 0x50 && tmp[1] === 0x4b) { //ZIP
this.modeZIP = true;
tmp[2] = this.readByte();
tmp[3] = this.readByte();
if (tmp[2] === 0x03 && tmp[3] === 0x04) {
//MODE_ZIP
tmp[0] = this.readByte();
tmp[1] = this.readByte();
// if (this.debug) alert("ZIP-Version: "+tmp[1]+" "+tmp[0]/10+"."+tmp[0]%10);
this.gpflags = this.readByte();
this.gpflags |= (this.readByte() << 8);
// if (this.debug) alert("gpflags: "+this.gpflags);
var method = this.readByte();
method |= (this.readByte() << 8);
// if (this.debug) alert("method: "+method);
this.readByte();
this.readByte();
this.readByte();
this.readByte();
// var crc = this.readByte();
// crc |= (this.readByte()<<8);
// crc |= (this.readByte()<<16);
// crc |= (this.readByte()<<24);
var compSize = this.readByte();
compSize |= (this.readByte() << 8);
compSize |= (this.readByte() << 16);
compSize |= (this.readByte() << 24);
var size = this.readByte();
size |= (this.readByte() << 8);
size |= (this.readByte() << 16);
size |= (this.readByte() << 24);
// if (this.debug) alert("local CRC: "+crc+"\nlocal Size: "+size+"\nlocal CompSize: "+compSize);
var filelen = this.readByte();
filelen |= (this.readByte() << 8);
var extralen = this.readByte();
extralen |= (this.readByte() << 8);
// if (this.debug) alert("filelen "+filelen);
i = 0;
this.nameBuf = [];
while (filelen--) {
var c = this.readByte();
if (c === "/" | c === ":") {
i = 0;
} else if (i < GZip.NAMEMAX - 1) {
this.nameBuf[i++] = String.fromCharCode(c);
}
}
// if (this.debug) alert("nameBuf: "+this.nameBuf);
if (!this.fileout) this.fileout = this.nameBuf;
var i = 0;
while (i < extralen) {
c = this.readByte();
i++;
}
// if (size = 0 && this.fileOut.charAt(this.fileout.length-1)=="/"){
// //skipdir
// // if (this.debug) alert("skipdir");
// }
if (method === 8) {
this.DeflateLoop();
// if (this.debug) alert(this.outputArr.join(''));
this.unzipped[this.files] = [this.outputArr.join(''), this.nameBuf.join('')];
this.files++;
}
this.skipdir();
}
}
};
GZip.prototype.skipdir = function () {
var tmp = [];
var compSize, size, os, i, c;
if ((this.gpflags & 8)) {
tmp[0] = this.readByte();
tmp[1] = this.readByte();
tmp[2] = this.readByte();
tmp[3] = this.readByte();
// if (tmp[0] == 0x50 && tmp[1] == 0x4b && tmp[2] == 0x07 && tmp[3] == 0x08) {
// crc = this.readByte();
// crc |= (this.readByte()<<8);
// crc |= (this.readByte()<<16);
// crc |= (this.readByte()<<24);
// } else {
// crc = tmp[0] | (tmp[1]<<8) | (tmp[2]<<16) | (tmp[3]<<24);
// }
compSize = this.readByte();
compSize |= (this.readByte() << 8);
compSize |= (this.readByte() << 16);
compSize |= (this.readByte() << 24);
size = this.readByte();
size |= (this.readByte() << 8);
size |= (this.readByte() << 16);
size |= (this.readByte() << 24);
}
if (this.modeZIP) this.nextFile();
tmp[0] = this.readByte();
if (tmp[0] !== 8) {
// if (this.debug) alert("Unknown compression method!");
return 0;
}
this.gpflags = this.readByte();
// if (this.debug && (this.gpflags & ~(0x1f))) alert("Unknown flags set!");
this.readByte();
this.readByte();
this.readByte();
this.readByte();
this.readByte();
os = this.readByte();
if ((this.gpflags & 4)) {
tmp[0] = this.readByte();
tmp[2] = this.readByte();
this.len = tmp[0] + 256 * tmp[1];
// if (this.debug) alert("Extra field size: "+this.len);
for (i = 0; i < this.len; i++)
this.readByte();
}
if ((this.gpflags & 8)) {
i = 0;
this.nameBuf = [];
while (c = this.readByte()) {
if (c === "7" || c === ":")
i = 0;
if (i < GZip.NAMEMAX - 1)
this.nameBuf[i++] = c;
}
//this.nameBuf[i] = "\0";
// if (this.debug) alert("original file name: "+this.nameBuf);
}
if ((this.gpflags & 16)) {
while (c = this.readByte()) { // FIXME: looks like they read to the end of the stream, should be doable more efficiently
//FILE COMMENT
}
}
if ((this.gpflags & 2)) {
this.readByte();
this.readByte();
}
this.DeflateLoop();
// crc = this.readByte();
// crc |= (this.readByte()<<8);
// crc |= (this.readByte()<<16);
// crc |= (this.readByte()<<24);
size = this.readByte();
size |= (this.readByte() << 8);
size |= (this.readByte() << 16);
size |= (this.readByte() << 24);
if (this.modeZIP) this.nextFile();
};
module.exports = GZip;

55
engine/cocos2d/compression/zlib.min.js vendored Normal file
View File

@@ -0,0 +1,55 @@
/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */
(function() {'use strict';function i(a){throw a;}var r=void 0,v=!0,aa=this;function y(a,c){var b=a.split("."),e=aa;!(b[0]in e)&&e.execScript&&e.execScript("var "+b[0]);for(var f;b.length&&(f=b.shift());)!b.length&&c!==r?e[f]=c:e=e[f]?e[f]:e[f]={}};var H="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array;function ba(a){if("string"===typeof a){var c=a.split(""),b,e;b=0;for(e=c.length;b<e;b++)c[b]=(c[b].charCodeAt(0)&255)>>>0;a=c}for(var f=1,d=0,g=a.length,h,m=0;0<g;){h=1024<g?1024:g;g-=h;do f+=a[m++],d+=f;while(--h);f%=65521;d%=65521}return(d<<16|f)>>>0};function J(a,c){this.index="number"===typeof c?c:0;this.i=0;this.buffer=a instanceof(H?Uint8Array:Array)?a:new (H?Uint8Array:Array)(32768);2*this.buffer.length<=this.index&&i(Error("invalid index"));this.buffer.length<=this.index&&this.f()}J.prototype.f=function(){var a=this.buffer,c,b=a.length,e=new (H?Uint8Array:Array)(b<<1);if(H)e.set(a);else for(c=0;c<b;++c)e[c]=a[c];return this.buffer=e};
J.prototype.d=function(a,c,b){var e=this.buffer,f=this.index,d=this.i,g=e[f],h;b&&1<c&&(a=8<c?(N[a&255]<<24|N[a>>>8&255]<<16|N[a>>>16&255]<<8|N[a>>>24&255])>>32-c:N[a]>>8-c);if(8>c+d)g=g<<c|a,d+=c;else for(h=0;h<c;++h)g=g<<1|a>>c-h-1&1,8===++d&&(d=0,e[f++]=N[g],g=0,f===e.length&&(e=this.f()));e[f]=g;this.buffer=e;this.i=d;this.index=f};J.prototype.finish=function(){var a=this.buffer,c=this.index,b;0<this.i&&(a[c]<<=8-this.i,a[c]=N[a[c]],c++);H?b=a.subarray(0,c):(a.length=c,b=a);return b};
var ca=new (H?Uint8Array:Array)(256),ha;for(ha=0;256>ha;++ha){for(var R=ha,ia=R,ja=7,R=R>>>1;R;R>>>=1)ia<<=1,ia|=R&1,--ja;ca[ha]=(ia<<ja&255)>>>0}var N=ca;var ka=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,
2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,
2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,
2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,
3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,
936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];H&&new Uint32Array(ka);function la(a){this.buffer=new (H?Uint16Array:Array)(2*a);this.length=0}la.prototype.getParent=function(a){return 2*((a-2)/4|0)};la.prototype.push=function(a,c){var b,e,f=this.buffer,d;b=this.length;f[this.length++]=c;for(f[this.length++]=a;0<b;)if(e=this.getParent(b),f[b]>f[e])d=f[b],f[b]=f[e],f[e]=d,d=f[b+1],f[b+1]=f[e+1],f[e+1]=d,b=e;else break;return this.length};
la.prototype.pop=function(){var a,c,b=this.buffer,e,f,d;c=b[0];a=b[1];this.length-=2;b[0]=b[this.length];b[1]=b[this.length+1];for(d=0;;){f=2*d+2;if(f>=this.length)break;f+2<this.length&&b[f+2]>b[f]&&(f+=2);if(b[f]>b[d])e=b[d],b[d]=b[f],b[f]=e,e=b[d+1],b[d+1]=b[f+1],b[f+1]=e;else break;d=f}return{index:a,value:c,length:this.length}};function S(a){var c=a.length,b=0,e=Number.POSITIVE_INFINITY,f,d,g,h,m,j,s,n,l;for(n=0;n<c;++n)a[n]>b&&(b=a[n]),a[n]<e&&(e=a[n]);f=1<<b;d=new (H?Uint32Array:Array)(f);g=1;h=0;for(m=2;g<=b;){for(n=0;n<c;++n)if(a[n]===g){j=0;s=h;for(l=0;l<g;++l)j=j<<1|s&1,s>>=1;for(l=j;l<f;l+=m)d[l]=g<<16|n;++h}++g;h<<=1;m<<=1}return[d,b,e]};function ma(a,c){this.h=pa;this.w=0;this.input=a;this.b=0;c&&(c.lazy&&(this.w=c.lazy),"number"===typeof c.compressionType&&(this.h=c.compressionType),c.outputBuffer&&(this.a=H&&c.outputBuffer instanceof Array?new Uint8Array(c.outputBuffer):c.outputBuffer),"number"===typeof c.outputIndex&&(this.b=c.outputIndex));this.a||(this.a=new (H?Uint8Array:Array)(32768))}var pa=2,qa={NONE:0,r:1,j:pa,N:3},ra=[],T;
for(T=0;288>T;T++)switch(v){case 143>=T:ra.push([T+48,8]);break;case 255>=T:ra.push([T-144+400,9]);break;case 279>=T:ra.push([T-256+0,7]);break;case 287>=T:ra.push([T-280+192,8]);break;default:i("invalid literal: "+T)}
ma.prototype.n=function(){var a,c,b,e,f=this.input;switch(this.h){case 0:b=0;for(e=f.length;b<e;){c=H?f.subarray(b,b+65535):f.slice(b,b+65535);b+=c.length;var d=c,g=b===e,h=r,m=r,j=r,s=r,n=r,l=this.a,q=this.b;if(H){for(l=new Uint8Array(this.a.buffer);l.length<=q+d.length+5;)l=new Uint8Array(l.length<<1);l.set(this.a)}h=g?1:0;l[q++]=h|0;m=d.length;j=~m+65536&65535;l[q++]=m&255;l[q++]=m>>>8&255;l[q++]=j&255;l[q++]=j>>>8&255;if(H)l.set(d,q),q+=d.length,l=l.subarray(0,q);else{s=0;for(n=d.length;s<n;++s)l[q++]=
d[s];l.length=q}this.b=q;this.a=l}break;case 1:var E=new J(new Uint8Array(this.a.buffer),this.b);E.d(1,1,v);E.d(1,2,v);var t=sa(this,f),z,K,A;z=0;for(K=t.length;z<K;z++)if(A=t[z],J.prototype.d.apply(E,ra[A]),256<A)E.d(t[++z],t[++z],v),E.d(t[++z],5),E.d(t[++z],t[++z],v);else if(256===A)break;this.a=E.finish();this.b=this.a.length;break;case pa:var x=new J(new Uint8Array(this.a),this.b),B,k,p,D,C,da=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],W,Ma,ea,Na,na,va=Array(19),Oa,$,oa,F,Pa;B=pa;x.d(1,
1,v);x.d(B,2,v);k=sa(this,f);W=ta(this.L,15);Ma=ua(W);ea=ta(this.K,7);Na=ua(ea);for(p=286;257<p&&0===W[p-1];p--);for(D=30;1<D&&0===ea[D-1];D--);var Qa=p,Ra=D,M=new (H?Uint32Array:Array)(Qa+Ra),u,O,w,fa,L=new (H?Uint32Array:Array)(316),I,G,P=new (H?Uint8Array:Array)(19);for(u=O=0;u<Qa;u++)M[O++]=W[u];for(u=0;u<Ra;u++)M[O++]=ea[u];if(!H){u=0;for(fa=P.length;u<fa;++u)P[u]=0}u=I=0;for(fa=M.length;u<fa;u+=O){for(O=1;u+O<fa&&M[u+O]===M[u];++O);w=O;if(0===M[u])if(3>w)for(;0<w--;)L[I++]=0,P[0]++;else for(;0<
w;)G=138>w?w:138,G>w-3&&G<w&&(G=w-3),10>=G?(L[I++]=17,L[I++]=G-3,P[17]++):(L[I++]=18,L[I++]=G-11,P[18]++),w-=G;else if(L[I++]=M[u],P[M[u]]++,w--,3>w)for(;0<w--;)L[I++]=M[u],P[M[u]]++;else for(;0<w;)G=6>w?w:6,G>w-3&&G<w&&(G=w-3),L[I++]=16,L[I++]=G-3,P[16]++,w-=G}a=H?L.subarray(0,I):L.slice(0,I);na=ta(P,7);for(F=0;19>F;F++)va[F]=na[da[F]];for(C=19;4<C&&0===va[C-1];C--);Oa=ua(na);x.d(p-257,5,v);x.d(D-1,5,v);x.d(C-4,4,v);for(F=0;F<C;F++)x.d(va[F],3,v);F=0;for(Pa=a.length;F<Pa;F++)if($=a[F],x.d(Oa[$],
na[$],v),16<=$){F++;switch($){case 16:oa=2;break;case 17:oa=3;break;case 18:oa=7;break;default:i("invalid code: "+$)}x.d(a[F],oa,v)}var Sa=[Ma,W],Ta=[Na,ea],Q,Ua,ga,ya,Va,Wa,Xa,Ya;Va=Sa[0];Wa=Sa[1];Xa=Ta[0];Ya=Ta[1];Q=0;for(Ua=k.length;Q<Ua;++Q)if(ga=k[Q],x.d(Va[ga],Wa[ga],v),256<ga)x.d(k[++Q],k[++Q],v),ya=k[++Q],x.d(Xa[ya],Ya[ya],v),x.d(k[++Q],k[++Q],v);else if(256===ga)break;this.a=x.finish();this.b=this.a.length;break;default:i("invalid compression type")}return this.a};
function wa(a,c){this.length=a;this.G=c}
function xa(){var a=za;switch(v){case 3===a:return[257,a-3,0];case 4===a:return[258,a-4,0];case 5===a:return[259,a-5,0];case 6===a:return[260,a-6,0];case 7===a:return[261,a-7,0];case 8===a:return[262,a-8,0];case 9===a:return[263,a-9,0];case 10===a:return[264,a-10,0];case 12>=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272,a-
31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:i("invalid length: "+a)}}var Aa=[],za,Ba;
for(za=3;258>=za;za++)Ba=xa(),Aa[za]=Ba[2]<<24|Ba[1]<<16|Ba[0];var Ca=H?new Uint32Array(Aa):Aa;
function sa(a,c){function b(a,c){var b=a.G,d=[],e=0,f;f=Ca[a.length];d[e++]=f&65535;d[e++]=f>>16&255;d[e++]=f>>24;var g;switch(v){case 1===b:g=[0,b-1,0];break;case 2===b:g=[1,b-2,0];break;case 3===b:g=[2,b-3,0];break;case 4===b:g=[3,b-4,0];break;case 6>=b:g=[4,b-5,1];break;case 8>=b:g=[5,b-7,1];break;case 12>=b:g=[6,b-9,2];break;case 16>=b:g=[7,b-13,2];break;case 24>=b:g=[8,b-17,3];break;case 32>=b:g=[9,b-25,3];break;case 48>=b:g=[10,b-33,4];break;case 64>=b:g=[11,b-49,4];break;case 96>=b:g=[12,b-
65,5];break;case 128>=b:g=[13,b-97,5];break;case 192>=b:g=[14,b-129,6];break;case 256>=b:g=[15,b-193,6];break;case 384>=b:g=[16,b-257,7];break;case 512>=b:g=[17,b-385,7];break;case 768>=b:g=[18,b-513,8];break;case 1024>=b:g=[19,b-769,8];break;case 1536>=b:g=[20,b-1025,9];break;case 2048>=b:g=[21,b-1537,9];break;case 3072>=b:g=[22,b-2049,10];break;case 4096>=b:g=[23,b-3073,10];break;case 6144>=b:g=[24,b-4097,11];break;case 8192>=b:g=[25,b-6145,11];break;case 12288>=b:g=[26,b-8193,12];break;case 16384>=
b:g=[27,b-12289,12];break;case 24576>=b:g=[28,b-16385,13];break;case 32768>=b:g=[29,b-24577,13];break;default:i("invalid distance")}f=g;d[e++]=f[0];d[e++]=f[1];d[e++]=f[2];var h,j;h=0;for(j=d.length;h<j;++h)l[q++]=d[h];t[d[0]]++;z[d[3]]++;E=a.length+c-1;n=null}var e,f,d,g,h,m={},j,s,n,l=H?new Uint16Array(2*c.length):[],q=0,E=0,t=new (H?Uint32Array:Array)(286),z=new (H?Uint32Array:Array)(30),K=a.w,A;if(!H){for(d=0;285>=d;)t[d++]=0;for(d=0;29>=d;)z[d++]=0}t[256]=1;e=0;for(f=c.length;e<f;++e){d=h=0;
for(g=3;d<g&&e+d!==f;++d)h=h<<8|c[e+d];m[h]===r&&(m[h]=[]);j=m[h];if(!(0<E--)){for(;0<j.length&&32768<e-j[0];)j.shift();if(e+3>=f){n&&b(n,-1);d=0;for(g=f-e;d<g;++d)A=c[e+d],l[q++]=A,++t[A];break}if(0<j.length){var x=r,B=r,k=0,p=r,D=r,C=r,da=r,W=c.length,D=0,da=j.length;a:for(;D<da;D++){x=j[da-D-1];p=3;if(3<k){for(C=k;3<C;C--)if(c[x+C-1]!==c[e+C-1])continue a;p=k}for(;258>p&&e+p<W&&c[x+p]===c[e+p];)++p;p>k&&(B=x,k=p);if(258===p)break}s=new wa(k,e-B);n?n.length<s.length?(A=c[e-1],l[q++]=A,++t[A],b(s,
0)):b(n,-1):s.length<K?n=s:b(s,0)}else n?b(n,-1):(A=c[e],l[q++]=A,++t[A])}j.push(e)}l[q++]=256;t[256]++;a.L=t;a.K=z;return H?l.subarray(0,q):l}
function ta(a,c){function b(a){var c=z[a][K[a]];c===n?(b(a+1),b(a+1)):--E[c];++K[a]}var e=a.length,f=new la(572),d=new (H?Uint8Array:Array)(e),g,h,m,j,s;if(!H)for(j=0;j<e;j++)d[j]=0;for(j=0;j<e;++j)0<a[j]&&f.push(j,a[j]);g=Array(f.length/2);h=new (H?Uint32Array:Array)(f.length/2);if(1===g.length)return d[f.pop().index]=1,d;j=0;for(s=f.length/2;j<s;++j)g[j]=f.pop(),h[j]=g[j].value;var n=h.length,l=new (H?Uint16Array:Array)(c),q=new (H?Uint8Array:Array)(c),E=new (H?Uint8Array:Array)(n),t=Array(c),z=
Array(c),K=Array(c),A=(1<<c)-n,x=1<<c-1,B,k,p,D,C;l[c-1]=n;for(k=0;k<c;++k)A<x?q[k]=0:(q[k]=1,A-=x),A<<=1,l[c-2-k]=(l[c-1-k]/2|0)+n;l[0]=q[0];t[0]=Array(l[0]);z[0]=Array(l[0]);for(k=1;k<c;++k)l[k]>2*l[k-1]+q[k]&&(l[k]=2*l[k-1]+q[k]),t[k]=Array(l[k]),z[k]=Array(l[k]);for(B=0;B<n;++B)E[B]=c;for(p=0;p<l[c-1];++p)t[c-1][p]=h[p],z[c-1][p]=p;for(B=0;B<c;++B)K[B]=0;1===q[c-1]&&(--E[0],++K[c-1]);for(k=c-2;0<=k;--k){D=B=0;C=K[k+1];for(p=0;p<l[k];p++)D=t[k+1][C]+t[k+1][C+1],D>h[B]?(t[k][p]=D,z[k][p]=n,C+=2):
(t[k][p]=h[B],z[k][p]=B,++B);K[k]=0;1===q[k]&&b(k)}m=E;j=0;for(s=g.length;j<s;++j)d[g[j].index]=m[j];return d}function ua(a){var c=new (H?Uint16Array:Array)(a.length),b=[],e=[],f=0,d,g,h,m;d=0;for(g=a.length;d<g;d++)b[a[d]]=(b[a[d]]|0)+1;d=1;for(g=16;d<=g;d++)e[d]=f,f+=b[d]|0,f>1<<d&&i("overcommitted"),f<<=1;65536>f&&i("undercommitted");d=0;for(g=a.length;d<g;d++){f=e[a[d]];e[a[d]]+=1;h=c[d]=0;for(m=a[d];h<m;h++)c[d]=c[d]<<1|f&1,f>>>=1}return c};function Da(a,c){this.input=a;this.a=new (H?Uint8Array:Array)(32768);this.h=U.j;var b={},e;if((c||!(c={}))&&"number"===typeof c.compressionType)this.h=c.compressionType;for(e in c)b[e]=c[e];b.outputBuffer=this.a;this.z=new ma(this.input,b)}var U=qa;
Da.prototype.n=function(){var a,c,b,e,f,d,g,h=0;g=this.a;a=Ea;switch(a){case Ea:c=Math.LOG2E*Math.log(32768)-8;break;default:i(Error("invalid compression method"))}b=c<<4|a;g[h++]=b;switch(a){case Ea:switch(this.h){case U.NONE:f=0;break;case U.r:f=1;break;case U.j:f=2;break;default:i(Error("unsupported compression type"))}break;default:i(Error("invalid compression method"))}e=f<<6|0;g[h++]=e|31-(256*b+e)%31;d=ba(this.input);this.z.b=h;g=this.z.n();h=g.length;H&&(g=new Uint8Array(g.buffer),g.length<=
h+4&&(this.a=new Uint8Array(g.length+4),this.a.set(g),g=this.a),g=g.subarray(0,h+4));g[h++]=d>>24&255;g[h++]=d>>16&255;g[h++]=d>>8&255;g[h++]=d&255;return g};y("Zlib.Deflate",Da);y("Zlib.Deflate.compress",function(a,c){return(new Da(a,c)).n()});y("Zlib.Deflate.CompressionType",U);y("Zlib.Deflate.CompressionType.NONE",U.NONE);y("Zlib.Deflate.CompressionType.FIXED",U.r);y("Zlib.Deflate.CompressionType.DYNAMIC",U.j);function V(a,c){this.k=[];this.l=32768;this.e=this.g=this.c=this.q=0;this.input=H?new Uint8Array(a):a;this.s=!1;this.m=Fa;this.B=!1;if(c||!(c={}))c.index&&(this.c=c.index),c.bufferSize&&(this.l=c.bufferSize),c.bufferType&&(this.m=c.bufferType),c.resize&&(this.B=c.resize);switch(this.m){case Ga:this.b=32768;this.a=new (H?Uint8Array:Array)(32768+this.l+258);break;case Fa:this.b=0;this.a=new (H?Uint8Array:Array)(this.l);this.f=this.J;this.t=this.H;this.o=this.I;break;default:i(Error("invalid inflate mode"))}}
var Ga=0,Fa=1,Ha={D:Ga,C:Fa};
V.prototype.p=function(){for(;!this.s;){var a=X(this,3);a&1&&(this.s=v);a>>>=1;switch(a){case 0:var c=this.input,b=this.c,e=this.a,f=this.b,d=r,g=r,h=r,m=e.length,j=r;this.e=this.g=0;d=c[b++];d===r&&i(Error("invalid uncompressed block header: LEN (first byte)"));g=d;d=c[b++];d===r&&i(Error("invalid uncompressed block header: LEN (second byte)"));g|=d<<8;d=c[b++];d===r&&i(Error("invalid uncompressed block header: NLEN (first byte)"));h=d;d=c[b++];d===r&&i(Error("invalid uncompressed block header: NLEN (second byte)"));h|=
d<<8;g===~h&&i(Error("invalid uncompressed block header: length verify"));b+g>c.length&&i(Error("input buffer is broken"));switch(this.m){case Ga:for(;f+g>e.length;){j=m-f;g-=j;if(H)e.set(c.subarray(b,b+j),f),f+=j,b+=j;else for(;j--;)e[f++]=c[b++];this.b=f;e=this.f();f=this.b}break;case Fa:for(;f+g>e.length;)e=this.f({v:2});break;default:i(Error("invalid inflate mode"))}if(H)e.set(c.subarray(b,b+g),f),f+=g,b+=g;else for(;g--;)e[f++]=c[b++];this.c=b;this.b=f;this.a=e;break;case 1:this.o(Ia,Ja);break;
case 2:Ka(this);break;default:i(Error("unknown BTYPE: "+a))}}return this.t()};
var La=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],Za=H?new Uint16Array(La):La,$a=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],ab=H?new Uint16Array($a):$a,bb=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],cb=H?new Uint8Array(bb):bb,db=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],eb=H?new Uint16Array(db):db,fb=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,
10,11,11,12,12,13,13],gb=H?new Uint8Array(fb):fb,hb=new (H?Uint8Array:Array)(288),Y,ib;Y=0;for(ib=hb.length;Y<ib;++Y)hb[Y]=143>=Y?8:255>=Y?9:279>=Y?7:8;var Ia=S(hb),jb=new (H?Uint8Array:Array)(30),kb,lb;kb=0;for(lb=jb.length;kb<lb;++kb)jb[kb]=5;var Ja=S(jb);function X(a,c){for(var b=a.g,e=a.e,f=a.input,d=a.c,g;e<c;)g=f[d++],g===r&&i(Error("input buffer is broken")),b|=g<<e,e+=8;g=b&(1<<c)-1;a.g=b>>>c;a.e=e-c;a.c=d;return g}
function mb(a,c){for(var b=a.g,e=a.e,f=a.input,d=a.c,g=c[0],h=c[1],m,j,s;e<h;)m=f[d++],m===r&&i(Error("input buffer is broken")),b|=m<<e,e+=8;j=g[b&(1<<h)-1];s=j>>>16;a.g=b>>s;a.e=e-s;a.c=d;return j&65535}
function Ka(a){function c(a,b,c){var d,e,f,g;for(g=0;g<a;)switch(d=mb(this,b),d){case 16:for(f=3+X(this,2);f--;)c[g++]=e;break;case 17:for(f=3+X(this,3);f--;)c[g++]=0;e=0;break;case 18:for(f=11+X(this,7);f--;)c[g++]=0;e=0;break;default:e=c[g++]=d}return c}var b=X(a,5)+257,e=X(a,5)+1,f=X(a,4)+4,d=new (H?Uint8Array:Array)(Za.length),g,h,m,j;for(j=0;j<f;++j)d[Za[j]]=X(a,3);g=S(d);h=new (H?Uint8Array:Array)(b);m=new (H?Uint8Array:Array)(e);a.o(S(c.call(a,b,g,h)),S(c.call(a,e,g,m)))}
V.prototype.o=function(a,c){var b=this.a,e=this.b;this.u=a;for(var f=b.length-258,d,g,h,m;256!==(d=mb(this,a));)if(256>d)e>=f&&(this.b=e,b=this.f(),e=this.b),b[e++]=d;else{g=d-257;m=ab[g];0<cb[g]&&(m+=X(this,cb[g]));d=mb(this,c);h=eb[d];0<gb[d]&&(h+=X(this,gb[d]));e>=f&&(this.b=e,b=this.f(),e=this.b);for(;m--;)b[e]=b[e++-h]}for(;8<=this.e;)this.e-=8,this.c--;this.b=e};
V.prototype.I=function(a,c){var b=this.a,e=this.b;this.u=a;for(var f=b.length,d,g,h,m;256!==(d=mb(this,a));)if(256>d)e>=f&&(b=this.f(),f=b.length),b[e++]=d;else{g=d-257;m=ab[g];0<cb[g]&&(m+=X(this,cb[g]));d=mb(this,c);h=eb[d];0<gb[d]&&(h+=X(this,gb[d]));e+m>f&&(b=this.f(),f=b.length);for(;m--;)b[e]=b[e++-h]}for(;8<=this.e;)this.e-=8,this.c--;this.b=e};
V.prototype.f=function(){var a=new (H?Uint8Array:Array)(this.b-32768),c=this.b-32768,b,e,f=this.a;if(H)a.set(f.subarray(32768,a.length));else{b=0;for(e=a.length;b<e;++b)a[b]=f[b+32768]}this.k.push(a);this.q+=a.length;if(H)f.set(f.subarray(c,c+32768));else for(b=0;32768>b;++b)f[b]=f[c+b];this.b=32768;return f};
V.prototype.J=function(a){var c,b=this.input.length/this.c+1|0,e,f,d,g=this.input,h=this.a;a&&("number"===typeof a.v&&(b=a.v),"number"===typeof a.F&&(b+=a.F));2>b?(e=(g.length-this.c)/this.u[2],d=258*(e/2)|0,f=d<h.length?h.length+d:h.length<<1):f=h.length*b;H?(c=new Uint8Array(f),c.set(h)):c=h;return this.a=c};
V.prototype.t=function(){var a=0,c=this.a,b=this.k,e,f=new (H?Uint8Array:Array)(this.q+(this.b-32768)),d,g,h,m;if(0===b.length)return H?this.a.subarray(32768,this.b):this.a.slice(32768,this.b);d=0;for(g=b.length;d<g;++d){e=b[d];h=0;for(m=e.length;h<m;++h)f[a++]=e[h]}d=32768;for(g=this.b;d<g;++d)f[a++]=c[d];this.k=[];return this.buffer=f};
V.prototype.H=function(){var a,c=this.b;H?this.B?(a=new Uint8Array(c),a.set(this.a.subarray(0,c))):a=this.a.subarray(0,c):(this.a.length>c&&(this.a.length=c),a=this.a);return this.buffer=a};function nb(a,c){var b,e;this.input=a;this.c=0;if(c||!(c={}))c.index&&(this.c=c.index),c.verify&&(this.M=c.verify);b=a[this.c++];e=a[this.c++];switch(b&15){case Ea:this.method=Ea;break;default:i(Error("unsupported compression method"))}0!==((b<<8)+e)%31&&i(Error("invalid fcheck flag:"+((b<<8)+e)%31));e&32&&i(Error("fdict flag is not supported"));this.A=new V(a,{index:this.c,bufferSize:c.bufferSize,bufferType:c.bufferType,resize:c.resize})}
nb.prototype.p=function(){var a=this.input,c,b;c=this.A.p();this.c=this.A.c;this.M&&(b=(a[this.c++]<<24|a[this.c++]<<16|a[this.c++]<<8|a[this.c++])>>>0,b!==ba(c)&&i(Error("invalid adler-32 checksum")));return c};y("Zlib.Inflate",nb);y("Zlib.Inflate.BufferType",Ha);Ha.ADAPTIVE=Ha.C;Ha.BLOCK=Ha.D;y("Zlib.Inflate.prototype.decompress",nb.prototype.p);var ob=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];H&&new Uint16Array(ob);var pb=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258];H&&new Uint16Array(pb);var qb=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0];H&&new Uint8Array(qb);var rb=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577];H&&new Uint16Array(rb);
var sb=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];H&&new Uint8Array(sb);var tb=new (H?Uint8Array:Array)(288),Z,ub;Z=0;for(ub=tb.length;Z<ub;++Z)tb[Z]=143>=Z?8:255>=Z?9:279>=Z?7:8;S(tb);var vb=new (H?Uint8Array:Array)(30),wb,xb;wb=0;for(xb=vb.length;wb<xb;++wb)vb[wb]=5;S(vb);var Ea=8;}).call(window);
//add for cocos2d-html5
var _p = window.Zlib;
_p.Deflate = _p["Deflate"];
_p.Deflate.compress = _p.Deflate["compress"];
_p.Inflate = _p["Inflate"];
_p.Inflate.BufferType = _p.Inflate["BufferType"];
_p.Inflate.prototype.decompress = _p.Inflate.prototype["decompress"];
module.exports = _p;

View File

@@ -0,0 +1,446 @@
/****************************************************************************
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import enums from '../../renderer/enums';
import Color from '../value-types/color';
import { toRadian } from '../value-types';
let RendererLight = null;
if (CC_JSB && CC_NATIVERENDERER) {
// @ts-ignore
RendererLight = window.renderer.Light;
} else {
// @ts-ignore
RendererLight = require('../../renderer/scene/light');
}
import renderer from '../renderer/index';
import Enum from '../platform/CCEnum';
import CCComponent from '../components/CCComponent';
import { ccclass, menu, inspector, property, executeInEditMode } from '../platform/CCClassDecorator';
/**
* !#en The light source type
*
* !#zh 光源类型
* @static
* @enum Light.Type
*/
const LightType = Enum({
/**
* !#en The direction of light
*
* !#zh 平行光
* @property {Number} DIRECTIONAL
* @readonly
*/
DIRECTIONAL: 0,
/**
* !#en The point of light
*
* !#zh 点光源
* @property {Number} POINT
* @readonly
*/
POINT: 1,
/**
* !#en The spot of light
*
* !#zh 聚光灯
* @property {Number} SPOT
* @readonly
*/
SPOT: 2,
/**
* !#en The ambient light
* !#zh 环境光
* @property {Number} AMBIENT
* @readonly
*/
AMBIENT: 3
});
/**
* !#en The shadow type
*
* !#zh 阴影类型
* @static
* @enum Light.ShadowType
*/
const LightShadowType = Enum({
/**
* !#en No shadows
*
* !#zh 阴影关闭
* @property NONE
* @readonly
* @type {Number}
*/
NONE: 0,
/**
* !#en Hard shadows
*
* !#zh 阴硬影
* @property HARD
* @readonly
* @type {Number}
*/
HARD: 2,
/**
* !#en Soft PCF 3x3 shadows
*
* !#zh PCF 3x3 软阴影
* @property SOFT_PCF3X3
* @readonly
* @type {Number}
*/
SOFT_PCF3X3: 3,
/**
* !#en Soft PCF 5x5 shadows
*
* !#zh PCF 5x5 软阴影
* @property SOFT_PCF5X5
* @readonly
* @type {Number}
*/
SOFT_PCF5X5: 4,
});
/**
* !#en The Light Component
*
* !#zh 光源组件
* @class Light
* @extends Component
*/
@ccclass('cc.Light')
@menu('i18n:MAIN_MENU.component.renderers/Light')
@executeInEditMode
@inspector('packages://inspector/inspectors/comps/light.js')
export default class Light extends CCComponent {
@property
_type = LightType.DIRECTIONAL;
@property
_color = Color.WHITE;
@property
_intensity = 1;
@property
_range = 1000;
@property
_spotAngle = 60;
@property
_spotExp = 1;
@property
_shadowType = LightShadowType.NONE;
@property
_shadowResolution = 1024;
@property
_shadowDarkness = 0.5;
@property
_shadowMinDepth = 1;
@property
_shadowMaxDepth = 4096;
@property
_shadowFrustumSize = 1024;
@property
_shadowBias = 0.0005;
/**
* !#en The light source typecurrently we have directional, point, spot three type.
* !#zh 光源类型,目前有 平行光,聚光灯,点光源 三种类型
* @type {LightType}
*/
@property({
type: LightType
})
get type() {
return this._type;
}
set type(val) {
this._type = val;
let type = enums.LIGHT_DIRECTIONAL;
if (val === LightType.POINT) {
type = enums.LIGHT_POINT;
} else if (val === LightType.SPOT) {
type = enums.LIGHT_SPOT;
}
else if (val === LightType.AMBIENT) {
type = enums.LIGHT_AMBIENT;
}
this._light.setType(type);
}
/**
* !#en The light source color
* !#zh 光源颜色
* @type {Color}
*/
@property
get color() {
return this._color;
}
set color(val) {
if (!this._color.equals(val)) {
this._color.set(val);
}
this._light.setColor(val.r / 255, val.g / 255, val.b / 255);
}
/**
* !#en The light source intensity
*
* !#zh 光源强度
* @type {Number}
*/
@property
get intensity() {
return this._intensity;
}
set intensity(val) {
this._intensity = val;
this._light.setIntensity(val);
}
/**
* !#en The light range, used for spot and point light
*
* !#zh 针对聚光灯和点光源设置光源范围
* @type {Number}
*/
@property
get range() {
return this._range;
}
set range(val) {
this._range = val;
this._light.setRange(val);
}
/**
* !#en The spot light cone angle
*
* !#zh 聚光灯锥角
* @type {Number}
*/
@property
get spotAngle() {
return this._spotAngle;
}
set spotAngle(val) {
this._spotAngle = val;
this._light.setSpotAngle(toRadian(val));
}
/**
* !#en The spot light exponential
*
* !#zh 聚光灯指数
* @type {Number}
*/
@property
get spotExp() {
return this._spotExp;
}
set spotExp(val) {
this._spotExp = val;
this._light.setSpotExp(val);
}
/**
* !#en The shadow type
*
* !#zh 阴影类型
* @type {Number} shadowType
*/
@property({
type: LightShadowType
})
get shadowType() {
return this._shadowType;
}
set shadowType(val) {
this._shadowType = val;
this._light.setShadowType(val);
}
/**
* !#en The shadow resolution
*
* !#zh 阴影分辨率
*
* @type {Number}
*/
@property
get shadowResolution() {
return this._shadowResolution;
}
set shadowResolution(val) {
this._shadowResolution = val;
this._light.setShadowResolution(val);
}
/**
* !#en The shadow darkness
*
* !#zh 阴影灰度值
*
* @type {Number}
*/
@property
get shadowDarkness() {
return this._shadowDarkness;
}
set shadowDarkness(val) {
this._shadowDarkness = val;
this._light.setShadowDarkness(val);
}
/**
* !#en The shadow min depth
*
* !#zh 阴影最小深度
*
* @type {Number}
*/
@property
get shadowMinDepth() {
return this._shadowMinDepth;
}
set shadowMinDepth(val) {
this._shadowMinDepth = val;
this._light.setShadowMinDepth(val);
}
/**
* !#en The shadow max depth
*
* !#zh 阴影最大深度
*
* @type {Number}
*/
@property
get shadowMaxDepth() {
return this._shadowMaxDepth;
}
set shadowMaxDepth(val) {
this._shadowMaxDepth = val;
this._light.setShadowMaxDepth(val);
}
/**
* !#en The shadow frustum size
*
* !#zh 阴影截锥体大小
*
* @type {Number}
*/
@property
get shadowFrustumSize() {
return this._shadowFrustumSize;
}
set shadowFrustumSize(val) {
this._shadowFrustumSize = val;
this._light.setShadowFrustumSize(val);
}
// /**
// * !#en The shadow bias
// *
// * !#zh 阴影偏移量
// *
// * @type {Number}
// */
// @property
// get shadowBias() {
// return this._shadowBias;
// }
// set shadowBias(val) {
// this._shadowBias = val;
// this._light.setShadowBias(val);
// }
static Type = LightType;
static ShadowType = LightShadowType;
constructor() {
super();
this._light = new RendererLight();
}
onLoad() {
this._light.setNode(this.node);
this.type = this._type;
this.color = this._color;
this.intensity = this._intensity;
this.range = this._range;
this.spotAngle = this._spotAngle;
this.spotExp = this._spotExp;
this.shadowType = this._shadowType;
this.shadowResolution = this._shadowResolution;
this.shadowDarkness = this._shadowDarkness;
this.shadowMaxDepth = this._shadowMaxDepth;
this.shadowFrustumSize = this._shadowFrustumSize;
this.shadowBias = this._shadowBias;
}
onEnable() {
renderer.scene.addLight(this._light);
}
onDisable() {
renderer.scene.removeLight(this._light);
}
}
cc.Light = Light;

View File

@@ -0,0 +1,90 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
let Model = cc.Class({
name: 'cc.Model',
extends: cc.Asset,
ctor () {
this._rootNode = null;
this.loaded = false;
},
properties: {
_nodes: {
default: []
},
_precomputeJointMatrix: false,
nodes: {
get () {
return this._nodes;
}
},
rootNode: {
get () {
return this._rootNode;
}
},
precomputeJointMatrix: {
get () {
return this._precomputeJointMatrix;
}
}
},
onLoad () {
let nodes = this._nodes;
this._rootNode = nodes[0];
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
node.position = cc.v3.apply(this, node.position);
node.scale = cc.v3.apply(this, node.scale);
node.quat = cc.quat.apply(this, node.quat);
if (node.uniqueBindPose) {
node.uniqueBindPose = cc.mat4.apply(this, node.uniqueBindPose);
}
let pose = node.bindpose;
if (pose) {
for (let i in pose) {
pose[i] = cc.mat4.apply(this, pose[i]);
}
}
let children = node.children;
if (children) {
for (let i = 0; i < children.length; i++) {
children[i] = nodes[children[i]];
}
}
}
}
});
cc.Model = module.exports = Model;

View File

@@ -0,0 +1,214 @@
import Quat from '../value-types/quat';
import Vec3 from '../value-types/vec3';
let _quat_tmp = cc.quat();
let _vec3_tmp = cc.v3();
/*
* Rotates a Node object to a certain angle by modifying its quaternion property. <br/>
* The direction will be decided by the shortest angle.
* @class Rotate3DTo
* @extends ActionInterval
* @param {Number} duration duration in seconds
* @param {Number|Vec3} dstAngleX dstAngleX in degrees.
* @param {Number} [dstAngleY] dstAngleY in degrees.
* @param {Number} [dstAngleZ] dstAngleZ in degrees.
* @example
* var rotate3DTo = new cc.Rotate3DTo(2, cc.v3(0, 180, 0));
*/
cc.Rotate3DTo = cc.Class({
name: 'cc.Rotate3DTo',
extends: cc.ActionInterval,
ctor:function (duration, dstAngleX, dstAngleY, dstAngleZ) {
this._startQuat = cc.quat();
this._dstQuat = cc.quat();
dstAngleX !== undefined && this.initWithDuration(duration, dstAngleX, dstAngleY, dstAngleZ);
},
/*
* Initializes the action.
* @param {Number} duration
* @param {Number|Vec3|Quat} dstAngleX
* @param {Number} dstAngleY
* @param {Number} dstAngleZ
* @return {Boolean}
*/
initWithDuration:function (duration, dstAngleX, dstAngleY, dstAngleZ) {
if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) {
let dstQuat = this._dstQuat;
if (dstAngleX instanceof cc.Quat) {
dstQuat.set(dstAngleX);
}
else {
if (dstAngleX instanceof cc.Vec3) {
dstAngleY = dstAngleX.y;
dstAngleZ = dstAngleX.z;
dstAngleX = dstAngleX.x;
}
else {
dstAngleY = dstAngleY || 0;
dstAngleZ = dstAngleZ || 0;
}
Quat.fromEuler(dstQuat, dstAngleX, dstAngleY, dstAngleZ);
}
return true;
}
return false;
},
clone:function () {
var action = new cc.Rotate3DTo();
this._cloneDecoration(action);
action.initWithDuration(this._duration, this._dstQuat);
return action;
},
startWithTarget:function (target) {
cc.ActionInterval.prototype.startWithTarget.call(this, target);
this._startQuat.set(target.quat);
},
reverse:function () {
cc.logID(1016);
},
update:function (dt) {
dt = this._computeEaseTime(dt);
if (this.target) {
Quat.slerp(_quat_tmp, this._startQuat, this._dstQuat, dt);
this.target.setRotation(_quat_tmp);
}
}
});
/**
* !#en
* Rotates a Node object to a certain angle by modifying its quternion property. <br/>
* The direction will be decided by the shortest angle.
* !#zh 旋转到目标角度,通过逐帧修改它的 quternion 属性,旋转方向将由最短的角度决定。
* @method rotate3DTo
* @param {Number} duration duration in seconds
* @param {Number|Vec3|Quat} dstAngleX dstAngleX in degrees.
* @param {Number} [dstAngleY] dstAngleY in degrees.
* @param {Number} [dstAngleZ] dstAngleZ in degrees.
* @return {ActionInterval}
* @example
* // example
* var rotate3DTo = cc.rotate3DTo(2, cc.v3(0, 180, 0));
*/
cc.rotate3DTo = function (duration, dstAngleX, dstAngleY, dstAngleZ) {
return new cc.Rotate3DTo(duration, dstAngleX, dstAngleY, dstAngleZ);
};
/*
* Rotates a Node object counter clockwise a number of degrees by modifying its quaternion property.
* Relative to its properties to modify.
* @class Rotate3DBy
* @extends ActionInterval
* @param {Number} duration duration in seconds
* @param {Number|Vec3} deltaAngleX deltaAngleX in degrees
* @param {Number} [deltaAngleY] deltaAngleY in degrees
* @param {Number} [deltaAngleZ] deltaAngleZ in degrees
* @example
* var actionBy = new cc.Rotate3DBy(2, cc.v3(0, 360, 0));
*/
cc.Rotate3DBy = cc.Class({
name: 'cc.Rotate3DBy',
extends: cc.ActionInterval,
ctor: function (duration, deltaAngleX, deltaAngleY, deltaAngleZ) {
this._startQuat = cc.quat();
this._dstQuat = cc.quat();
this._deltaAngle = cc.v3();
deltaAngleX !== undefined && this.initWithDuration(duration, deltaAngleX, deltaAngleY, deltaAngleZ);
},
/*
* Initializes the action.
* @param {Number} duration duration in seconds
* @param {Number|Vec3} deltaAngleX deltaAngleX in degrees
* @param {Number} [deltaAngleY=] deltaAngleY in degrees
* @param {Number} [deltaAngleZ=] deltaAngleZ in degrees
* @return {Boolean}
*/
initWithDuration:function (duration, deltaAngleX, deltaAngleY, deltaAngleZ) {
if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) {
if (deltaAngleX instanceof cc.Vec3) {
deltaAngleY = deltaAngleX.y;
deltaAngleZ = deltaAngleX.z;
deltaAngleX = deltaAngleX.x;
}
else {
deltaAngleY = deltaAngleY || 0;
deltaAngleZ = deltaAngleZ || 0;
}
Vec3.set(this._deltaAngle, deltaAngleX, deltaAngleY, deltaAngleZ);
return true;
}
return false;
},
clone:function () {
var action = new cc.Rotate3DBy();
this._cloneDecoration(action);
action.initWithDuration(this._duration, this._angle);
return action;
},
startWithTarget:function (target) {
cc.ActionInterval.prototype.startWithTarget.call(this, target);
let startAngle = target.eulerAngles;
let deltaAngle = this._deltaAngle;
Quat.fromEuler(this._dstQuat, startAngle.x + deltaAngle.x, startAngle.y + deltaAngle.y, startAngle.z + deltaAngle.z);
this._startQuat.set(target.quat);
},
update: (function(){
let RAD = Math.PI / 180;
return function (dt) {
dt = this._computeEaseTime(dt);
if (this.target) {
Quat.slerp(_quat_tmp, this._startQuat, this._dstQuat, dt);
this.target.setRotation(_quat_tmp);
}
}
})(),
reverse:function () {
let angle = this._angle;
_vec3_tmp.x = -angle.x;
_vec3_tmp.y = -angle.y;
_vec3_tmp.z = -angle.z;
var action = new cc.Rotate3DBy(this._duration, _vec3_tmp);
this._cloneDecoration(action);
this._reverseEaseList(action);
return action;
}
});
/**
* !#en
* Rotates a Node object counter clockwise a number of degrees by modifying its quaternion property.
* Relative to its properties to modify.
* !#zh 旋转指定的 3D 角度。
* @method rotate3DBy
* @param {Number} duration duration in seconds
* @param {Number|Vec3} deltaAngleX deltaAngleX in degrees
* @param {Number} [deltaAngleY] deltaAngleY in degrees
* @param {Number} [deltaAngleZ] deltaAngleZ in degrees
* @return {ActionInterval}
* @example
* // example
* var actionBy = cc.rotate3DBy(2, cc.v3(0, 360, 0));
*/
cc.rotate3DBy = function (duration, deltaAngleX, deltaAngleY, deltaAngleZ) {
return new cc.Rotate3DBy(duration, deltaAngleX, deltaAngleY, deltaAngleZ);
};

View File

@@ -0,0 +1,22 @@
if (!CC_TEST && (!CC_EDITOR || !Editor.isMainProcess)) {
require('./primitive');
require('./physics/exports/physics-builtin');
require('./physics/exports/physics-cannon');
require('./physics/exports/physics-framework');
}
require('./CCModel');
require('./skeleton/CCSkeleton');
require('./skeleton/CCSkeletonAnimationClip');
require('./actions');
require('./physics/framework/assets/physics-material');
if (!CC_EDITOR || !Editor.isMainProcess) {
require('./skeleton/CCSkeletonAnimation');
require('./skeleton/CCSkinnedMeshRenderer');
require('./skeleton/skinned-mesh-renderer');
require('./CCLightComponent');
require('./particle/particle-system-3d');
require('./particle/renderer/particle-system-3d-renderer');
}

View File

@@ -0,0 +1,39 @@
import { ccclass, property } from '../../../platform/CCClassDecorator'
import { pseudoRandom, Color } from '../../../value-types';
import GradientRange from './gradient-range';
const COLOR_OVERTIME_RAND_OFFSET = 91041;
/**
* !#en The color over time module of 3d particle.
* !#zh 3D 粒子颜色变化模块
* @class ColorOvertimeModule
*/
@ccclass('cc.ColorOvertimeModule')
export default class ColorOvertimeModule {
/**
* !#en The enable of ColorOvertimeModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
enable = false;
/**
* !#en The parameter of color change over time, the linear difference between each key changes.
* !#zh 颜色随时间变化的参数,各个 key 之间线性差值变化。
* @type {GradientRange} color
*/
@property({
type: GradientRange,
})
color = new GradientRange();
animate (particle) {
if (this.enable) {
particle.color.set(particle.startColor);
particle.color.multiply(this.color.evaluate(1.0 - particle.remainingLifetime / particle.startLifetime, pseudoRandom(particle.randomSeed + COLOR_OVERTIME_RAND_OFFSET)));
}
}
}

View File

@@ -0,0 +1,136 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import Enum from '../../../platform/CCEnum';
import { lerp } from '../../../value-types';
import { AnimationCurve } from '../curve';
const SerializableTable = CC_EDITOR && [
[ "mode", "constant", "multiplier" ],
[ "mode", "curve", "multiplier" ],
[ "mode", "curveMin", "curveMax", "multiplier" ],
[ "mode", "constantMin", "constantMax", "multiplier"]
];
export const Mode = Enum({
Constant: 0,
Curve: 1,
TwoCurves: 2,
TwoConstants: 3,
});
/**
* !#en The curve range of target value.
* !#zh 目标值的曲线范围
* @class CurveRange
*/
@ccclass('cc.CurveRange')
export default class CurveRange {
static Mode = Mode;
/**
* !#en Curve type.
* !#zh 曲线类型。
* @property {Mode} mode
*/
@property({
type: Mode,
})
mode = Mode.Constant;
/**
* !#en The curve to use when mode is Curve.
* !#zh 当 mode 为 Curve 时,使用的曲线。
* @property {AnimationCurve} curve
*/
@property({
type: AnimationCurve,
})
curve = new AnimationCurve();
/**
* !#en The lower limit of the curve to use when mode is TwoCurves
* !#zh 当 mode 为 TwoCurves 时,使用的曲线下限。
* @property {AnimationCurve} curveMin
*/
@property({
type: AnimationCurve,
})
curveMin = new AnimationCurve();
/**
* !#en The upper limit of the curve to use when mode is TwoCurves
* !#zh 当 mode 为 TwoCurves 时,使用的曲线上限。
* @property {AnimationCurve} curveMax
*/
@property({
type: AnimationCurve,
})
curveMax = new AnimationCurve();
/**
* !#en When mode is Constant, the value of the curve.
* !#zh 当 mode 为 Constant 时,曲线的值。
* @property {Number} constant
*/
@property
constant = 0;
/**
* !#en The lower limit of the curve to use when mode is TwoConstants
* !#zh 当 mode 为 TwoConstants 时,曲线的下限。
* @property {Number} constantMin
*/
@property
constantMin = 0;
/**
* !#en The upper limit of the curve to use when mode is TwoConstants
* !#zh 当 mode 为 TwoConstants 时,曲线的上限。
* @property {Number} constantMax
*/
@property
constantMax = 0;
/**
* !#en Coefficients applied to curve interpolation.
* !#zh 应用于曲线插值的系数。
* @property {Number} multiplier
*/
@property
multiplier = 1;
constructor () {
}
evaluate (time, rndRatio) {
switch (this.mode) {
case Mode.Constant:
return this.constant;
case Mode.Curve:
return this.curve.evaluate(time) * this.multiplier;
case Mode.TwoCurves:
return lerp(this.curveMin.evaluate(time), this.curveMax.evaluate(time), rndRatio) * this.multiplier;
case Mode.TwoConstants:
return lerp(this.constantMin, this.constantMax, rndRatio);
}
}
getMax () {
switch (this.mode) {
case Mode.Constant:
return this.constant;
case Mode.Curve:
return this.multiplier;
case Mode.TwoConstants:
return this.constantMax;
case Mode.TwoCurves:
return this.multiplier;
}
return 0;
}
}
CC_EDITOR && (CurveRange.prototype._onBeforeSerialize = function(props){return SerializableTable[this.mode];});
cc.CurveRange = CurveRange;

View File

@@ -0,0 +1,96 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import { pseudoRandom, Quat, Vec3 } from '../../../value-types';
import { Space } from '../enum';
import { calculateTransform } from '../particle-general-function';
import CurveRange from './curve-range';
// tslint:disable: max-line-length
const FORCE_OVERTIME_RAND_OFFSET = 212165;
const _temp_v3 = cc.v3();
/**
* !#en The force over time module of 3d particle.
* !#zh 3D 粒子的加速度模块
* @class ForceOvertimeModule
*/
@ccclass('cc.ForceOvertimeModule')
export default class ForceOvertimeModule {
/**
* !#en The enable of ColorOvertimeModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
enable = false;
/**
* !#en Coordinate system used in acceleration calculation.
* !#zh 加速度计算时采用的坐标系。
* @property {Space} space
*/
@property({
type: Space,
})
space = Space.Local;
/**
* !#en X-axis acceleration component.
* !#zh X 轴方向上的加速度分量。
* @property {CurveRange} x
*/
@property({
type: CurveRange,
range: [-1, 1],
})
x = new CurveRange();
/**
* !#en Y-axis acceleration component.
* !#zh Y 轴方向上的加速度分量。
* @property {CurveRange} y
*/
@property({
type: CurveRange,
range: [-1, 1],
})
y = new CurveRange();
/**
* !#en Z-axis acceleration component.
* !#zh Z 轴方向上的加速度分量。
* @property {CurveRange} z
*/
@property({
type: CurveRange,
range: [-1, 1],
displayOrder: 4,
})
z = new CurveRange();
// TODO:currently not supported
randomized = false;
rotation = null;
needTransform = false;
constructor () {
this.rotation = new Quat();
this.needTransform = false;
}
update (space, worldTransform) {
this.needTransform = calculateTransform(space, this.space, worldTransform, this.rotation);
}
animate (p, dt) {
const normalizedTime = 1 - p.remainingLifetime / p.startLifetime;
const force = Vec3.set(_temp_v3, this.x.evaluate(normalizedTime, pseudoRandom(p.randomSeed + FORCE_OVERTIME_RAND_OFFSET)), this.y.evaluate(normalizedTime, pseudoRandom(p.randomSeed + FORCE_OVERTIME_RAND_OFFSET)), this.z.evaluate(normalizedTime, pseudoRandom(p.randomSeed + FORCE_OVERTIME_RAND_OFFSET)));
if (this.needTransform) {
Vec3.transformQuat(force, force, this.rotation);
}
Vec3.scaleAndAdd(p.velocity, p.velocity, force, dt);
}
}

View File

@@ -0,0 +1,147 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import Enum from '../../../platform/CCEnum';
import { Color } from '../../../value-types';
import { Gradient, AlphaKey, ColorKey } from './gradient';
const GRADIENT_MODE_FIX = 0;
const GRADIENT_MODE_BLEND = 1;
const GRADIENT_RANGE_MODE_COLOR = 0;
const GRADIENT_RANGE_MODE_TWO_COLOR = 1;
const GRADIENT_RANGE_MODE_RANDOM_COLOR = 2;
const GRADIENT_RANGE_MODE_GRADIENT = 3;
const GRADIENT_RANGE_MODE_TWO_GRADIENT = 4;
const SerializableTable = CC_EDITOR && [
[ "_mode", "color" ],
[ "_mode", "gradient" ],
[ "_mode", "colorMin", "colorMax" ],
[ "_mode", "gradientMin", "gradientMax"],
[ "_mode", "gradient" ]
];
const Mode = Enum({
Color: 0,
Gradient: 1,
TwoColors: 2,
TwoGradients: 3,
RandomColor: 4,
});
/**
* !#en The gradient range of color.
* !#zh 颜色值的渐变范围
* @class GradientRange
*/
@ccclass('cc.GradientRange')
export default class GradientRange {
static Mode = Mode;
@property
_mode = Mode.Color;
/**
* !#en Gradient type.
* !#zh 渐变色类型。
* @property {Mode} mode
*/
@property({
type: Mode,
})
get mode () {
return this._mode;
}
set mode (m) {
if (CC_EDITOR) {
if (m === Mode.RandomColor) {
if (this.gradient.colorKeys.length === 0) {
this.gradient.colorKeys.push(new ColorKey());
}
if (this.gradient.alphaKeys.length === 0) {
this.gradient.alphaKeys.push(new AlphaKey());
}
}
}
this._mode = m;
}
@property
_color = cc.Color.WHITE.clone();
/**
* !#en The color when mode is Color.
* !#zh 当 mode 为 Color 时的颜色。
* @property {Color} color
*/
@property
color = cc.Color.WHITE.clone();
/**
* !#en Lower color limit when mode is TwoColors.
* !#zh 当 mode 为 TwoColors 时的颜色下限。
* @property {Color} colorMin
*/
@property
colorMin = cc.Color.WHITE.clone();
/**
* !#en Upper color limit when mode is TwoColors.
* !#zh 当 mode 为 TwoColors 时的颜色上限。
* @property {Color} colorMax
*/
@property
colorMax = cc.Color.WHITE.clone();
/**
* !#en Color gradient when mode is Gradient
* !#zh 当 mode 为 Gradient 时的颜色渐变。
* @property {Gradient} gradient
*/
@property({
type: Gradient,
})
gradient = new Gradient();
/**
* !#en Lower color gradient limit when mode is TwoGradients.
* !#zh 当 mode 为 TwoGradients 时的颜色渐变下限。
* @property {Gradient} gradientMin
*/
@property({
type: Gradient,
})
gradientMin = new Gradient();
/**
* !#en Upper color gradient limit when mode is TwoGradients.
* !#zh 当 mode 为 TwoGradients 时的颜色渐变上限。
* @property {Gradient} gradientMax
*/
@property({
type: Gradient,
})
gradientMax = new Gradient();
evaluate (time, rndRatio) {
switch (this._mode) {
case Mode.Color:
return this.color;
case Mode.TwoColors:
this.colorMin.lerp(this.colorMax, rndRatio, this._color);
return this._color;
case Mode.RandomColor:
return this.gradient.randomColor();
case Mode.Gradient:
return this.gradient.evaluate(time);
case Mode.TwoGradients:
this.gradientMin.evaluate(time).lerp(this.gradientMax.evaluate(time), rndRatio, this._color);
return this._color;
default:
return this.color;
}
}
}
CC_EDITOR && (GradientRange.prototype._onBeforeSerialize = function(props){return SerializableTable[this._mode];});
cc.GradientRange = GradientRange;

View File

@@ -0,0 +1,191 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import Enum from '../../../platform/CCEnum';
import { lerp, repeat } from '../../../value-types';
// tslint:disable: max-line-length
const Mode = Enum({
Blend: 0,
Fixed: 1,
});
/**
* !#en The color key of gradient.
* !#zh color 关键帧
* @class ColorKey
*/
@ccclass('cc.ColorKey')
export class ColorKey {
/**
* !#en Color value.
* !#zh 颜色值。
* @property {Color} color
*/
@property
color = cc.Color.WHITE.clone();
/**
* !#en Time value.
* !#zh 时间值。
* @property {Number} time
*/
@property
time = 0;
}
/**
* !#en The alpha key of gradient.
* !#zh alpha 关键帧
* @class AlphaKey
*/
@ccclass('cc.AlphaKey')
export class AlphaKey {
/**
* !#en Alpha value.
* !#zh 透明度。
* @property {Number} alpha
*/
@property
alpha = 1;
/**
* !#en Time.
* !#zh 时间帧。
* @property {Number} time
*/
@property
time = 0;
}
/**
* !#en The gradient data of color.
* !#zh 颜色渐变数据
* @class Gradient
*/
@ccclass('cc.Gradient')
export class Gradient {
static Mode = Mode;
/**
* !#en Array of color key.
* !#zh 颜色关键帧列表。
* @property {[ColorKey]} colorKeys
*/
@property({
type: [ColorKey],
})
colorKeys = new Array();
/**
* !#en Array of alpha key.
* !#zh 透明度关键帧列表。
* @property {[AlphaKey]} alphaKeys
*/
@property({
type: [AlphaKey],
})
alphaKeys = new Array();
/**
* !#en Blend mode.
* !#zh 混合模式。
* @property {Mode} mode
*/
@property({
type: Mode,
})
mode = Mode.Blend;
_color = null;
constructor () {
this._color = cc.Color.WHITE.clone();
}
setKeys (colorKeys, alphaKeys) {
this.colorKeys = colorKeys;
this.alphaKeys = alphaKeys;
}
sortKeys () {
if (this.colorKeys.length > 1) {
this.colorKeys.sort((a, b) => a.time - b.time);
}
if (this.alphaKeys.length > 1) {
this.alphaKeys.sort((a, b) => a.time - b.time);
}
}
evaluate (time) {
this.getRGB(time);
this._color._fastSetA(this.getAlpha(time));
return this._color;
}
randomColor () {
const c = this.colorKeys[Math.trunc(Math.random() * this.colorKeys.length)];
const a = this.alphaKeys[Math.trunc(Math.random() * this.alphaKeys.length)];
this._color.set(c.color);
this._color._fastSetA(a.alpha);
return this._color;
}
getRGB (time) {
if (this.colorKeys.length > 1) {
time = repeat(time, 1);
for (let i = 1; i < this.colorKeys.length; ++i) {
const preTime = this.colorKeys[i - 1].time;
const curTime = this.colorKeys[i].time;
if (time >= preTime && time < curTime) {
if (this.mode === Mode.Fixed) {
return this.colorKeys[i].color;
}
const factor = (time - preTime) / (curTime - preTime);
this.colorKeys[i - 1].color.lerp(this.colorKeys[i].color, factor, this._color);
return this._color;
}
}
const lastIndex = this.colorKeys.length - 1;
if (time < this.colorKeys[0].time) {
cc.Color.BLACK.lerp(this.colorKeys[0].color, time / this.colorKeys[0].time, this._color);
} else if (time > this.colorKeys[lastIndex].time) {
this.colorKeys[lastIndex].color.lerp(cc.Color.BLACK, (time - this.colorKeys[lastIndex].time) / (1 - this.colorKeys[lastIndex].time), this._color);
}
// console.warn('something went wrong. can not get gradient color.');
} else if (this.colorKeys.length === 1) {
this._color.set(this.colorKeys[0].color);
return this._color;
} else {
this._color.set(cc.Color.WHITE);
return this._color;
}
}
getAlpha (time) {
if (this.alphaKeys.length > 1) {
time = repeat(time, 1);
for (let i = 1; i < this.alphaKeys.length; ++i) {
const preTime = this.alphaKeys[i - 1].time;
const curTime = this.alphaKeys[i].time;
if (time >= preTime && time < curTime) {
if (this.mode === Mode.Fixed) {
return this.alphaKeys[i].alpha;
}
const factor = (time - preTime) / (curTime - preTime);
return lerp(this.alphaKeys[i - 1].alpha , this.alphaKeys[i].alpha , factor);
}
}
const lastIndex = this.alphaKeys.length - 1;
if (time < this.alphaKeys[0].time) {
return lerp(255, this.alphaKeys[0].alpha, time / this.alphaKeys[0].time);
} else if (time > this.alphaKeys[lastIndex].time) {
return lerp(this.alphaKeys[lastIndex].alpha, 255, (time - this.alphaKeys[lastIndex].time) / (1 - this.alphaKeys[lastIndex].time));
}
} else if (this.alphaKeys.length === 1) {
return this.alphaKeys[0].alpha;
} else {
return 255;
}
}
}
cc.ColorKey = ColorKey;
cc.AlphaKey = AlphaKey;
cc.Gradient = Gradient;

View File

@@ -0,0 +1,161 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import { lerp, pseudoRandom, Vec3, Quat } from '../../../value-types';
import { Space } from '../enum';
import CurveRange from './curve-range';
// tslint:disable: max-line-length
const LIMIT_VELOCITY_RAND_OFFSET = 23541;
const _temp_v3 = cc.v3();
const _temp_v3_1 = cc.v3();
function dampenBeyondLimit (vel, limit, dampen) {
const sgn = Math.sign(vel);
let abs = Math.abs(vel);
if (abs > limit) {
abs = lerp(abs, limit, dampen);
}
return abs * sgn;
}
/**
* !#en The limit velocity module of 3d particle.
* !#zh 3D 粒子的限速模块
* @class LimitVelocityOvertimeModule
*/
@ccclass('cc.LimitVelocityOvertimeModule')
export default class LimitVelocityOvertimeModule {
/**
* !#en The enable of LimitVelocityOvertimeModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
enable = false;
/**
* !#en The coordinate system used when calculating the lower speed limit.
* !#zh 计算速度下限时采用的坐标系。
* @property {Space} space
*/
@property({
type: Space,
})
space = Space.Local;
/**
* !#en Whether to limit the three axes separately.
* !#zh 是否三个轴分开限制。
* @property {Boolean} separateAxes
*/
@property
separateAxes = false;
/**
* !#en Lower speed limit
* !#zh 速度下限。
* @property {CurveRange} limit
*/
@property({
type: CurveRange,
range: [-1, 1],
visible: function (this) {
return !this.separateAxes;
}
})
limit = new CurveRange();
/**
* !#en Lower speed limit in X direction.
* !#zh X 轴方向上的速度下限。
* @property {CurveRange} limitX
*/
@property({
type: CurveRange,
range: [-1, 1],
visible: function (this) {
return this.separateAxes;
}
})
limitX = new CurveRange();
/**
* !#en Lower speed limit in Y direction.
* !#zh Y 轴方向上的速度下限。
* @property {CurveRange} limitY
*/
@property({
type: CurveRange,
range: [-1, 1],
visible: function (this) {
return this.separateAxes;
}
})
limitY = new CurveRange();
/**
* !#en Lower speed limit in Z direction.
* !#zh Z 轴方向上的速度下限。
* @property {CurveRange} limitZ
*/
@property({
type: CurveRange,
range: [-1, 1],
visible: function (this) {
return this.separateAxes;
}
})
limitZ = new CurveRange();
/**
* !#en Interpolation of current speed and lower speed limit.
* !#zh 当前速度与速度下限的插值。
* @property {Number} dampen
*/
@property
dampen = 3;
// TODO:functions related to drag are temporarily not supported
drag = null;
multiplyDragByParticleSize = false;
multiplyDragByParticleVelocity = false;
private rotation = null;
private needTransform = false;
constructor () {
this.rotation = new Quat();
this.needTransform = false;
}
update (space: number, worldTransform: Mat4) {
this.needTransform = calculateTransform(space, this.space, worldTransform, this.rotation);
}
animate (p) {
const normalizedTime = 1 - p.remainingLifetime / p.startLifetime;
const dampedVel = _temp_v3;
if (this.separateAxes) {
Vec3.set(_temp_v3_1, this.limitX.evaluate(normalizedTime, pseudoRandom(p.randomSeed + LIMIT_VELOCITY_RAND_OFFSET))!,
this.limitY.evaluate(normalizedTime, pseudoRandom(p.randomSeed + LIMIT_VELOCITY_RAND_OFFSET))!,
this.limitZ.evaluate(normalizedTime, pseudoRandom(p.randomSeed + LIMIT_VELOCITY_RAND_OFFSET))!);
if (this.needTransform) {
Vec3.transformQuat(_temp_v3_1, _temp_v3_1, this.rotation);
}
Vec3.set(dampedVel,
dampenBeyondLimit(p.ultimateVelocity.x, _temp_v3_1.x, this.dampen),
dampenBeyondLimit(p.ultimateVelocity.y, _temp_v3_1.y, this.dampen),
dampenBeyondLimit(p.ultimateVelocity.z, _temp_v3_1.z, this.dampen));
}
else {
Vec3.normalize(dampedVel, p.ultimateVelocity);
Vec3.scale(dampedVel, dampedVel, dampenBeyondLimit(p.ultimateVelocity.len(), this.limit.evaluate(normalizedTime, pseudoRandom(p.randomSeed + LIMIT_VELOCITY_RAND_OFFSET)), this.dampen));
}
Vec3.copy(p.ultimateVelocity, dampedVel);
}
}

View File

@@ -0,0 +1,203 @@
import { repeat } from '../../../value-types';
import { evalOptCurve, OptimizedKey } from '../curve';
import { Mode } from './curve-range';
const CURVE_MODE_CONSTANT = 0;
const CURVE_MODE_RANDOM_CONSTANT = 1;
const CURVE_MODE_CURVE = 2;
const CURVE_MODE_RANDOM_CURVE = 3;
const UNIFORM_CURVE_KEY_NUM = 8;
// calculate the coefficience of the first order integral of the curve
function integrateKeyframe (coef) {
coef[0] = coef[0] / 4;
coef[1] = coef[1] / 3;
coef[2] = coef[2] / 2;
coef[3] = coef[3];
return coef;
}
// calculate the coefficience of the second order integral of the curve
function integrateKeyframeTwice (coef) {
coef[0] = coef[0] / 20;
coef[1] = coef[1] / 12;
coef[2] = coef[2] / 6;
coef[3] = coef[3] / 2;
return coef;
}
/**
* !#en The optimized curve.
* !#zh 优化曲线
* @class OptimizedCurve
*/
export class OptimizedCurve {
optimizedKeys = [];
integral = [];
constructUniform = false;
coefUniform = null;
timeUniform = null;
integralUniform = null;
constructor (constructUniform = false) {
this.optimizedKeys = new Array(); // the i-th optimezed key stores coefficients of [i,i+1] segment in the original curve,so if the time of last key of the original key is 1,the last key won't be kept in the opt curve.
this.integral = new Array(); // the integral of the curve between 0 and corresponding key,the i-th integral corresponds to the i+1-th key in optimizedKeys (because the integral of the first key is always zero,the first key won't be stored)
this.constructUniform = constructUniform;
this.coefUniform = null;
this.timeUniform = null;
this.integralUniform = null;
}
buildCurve (animationCurve, multiplier = 1) {
const keyNum = animationCurve.keyFrames.length - 1;
let i = 0;
if (this.optimizedKeys.length < keyNum) {
const keyToAdd = keyNum - this.optimizedKeys.length;
for (i = 0; i < keyToAdd; i++) {
const optKey = new OptimizedKey();
this.optimizedKeys.push(optKey);
}
} else {
this.optimizedKeys.splice(keyNum);
}
if (animationCurve.keyFrames.length === 1) {
this.optimizedKeys[0].coefficient[3] = animationCurve.keyFrames[0].value * multiplier;
this.optimizedKeys[0].time = 0;
this.optimizedKeys[0].endTime = 1;
} else {
let keyOffset = 0;
if (animationCurve.keyFrames[0].time !== 0) {
this.optimizedKeys.unshift(new OptimizedKey());
this.optimizedKeys[0].time = 0;
this.optimizedKeys[0].endTime = animationCurve.keyFrames[0].time;
this.optimizedKeys[0].coefficient[3] = animationCurve.keyFrames[0].value;
keyOffset = 1;
}
for (i = 0; i < keyNum; i++) {
animationCurve.calcOptimizedKey(this.optimizedKeys[i + keyOffset], i, Math.min(i + 1, keyNum));
this.optimizedKeys[i + keyOffset].index += keyOffset;
}
if (animationCurve.keyFrames[animationCurve.keyFrames.length - 1].time !== 1) {
this.optimizedKeys.push(new OptimizedKey());
this.optimizedKeys[this.optimizedKeys.length - 1].time = animationCurve.keyFrames[animationCurve.keyFrames.length - 1].time;
this.optimizedKeys[this.optimizedKeys.length - 1].endTime = 1;
this.optimizedKeys[this.optimizedKeys.length - 1].coefficient[3] = animationCurve.keyFrames[animationCurve.keyFrames.length - 1].value;
}
}
for (i = 0; i < this.optimizedKeys.length; i++) {
this.optimizedKeys[i].coefficient[0] *= multiplier;
this.optimizedKeys[i].coefficient[1] *= multiplier;
this.optimizedKeys[i].coefficient[2] *= multiplier;
this.optimizedKeys[i].coefficient[3] *= multiplier;
}
if (this.constructUniform) {
this.coefUniform = new Float32Array(UNIFORM_CURVE_KEY_NUM * 4);
this.timeUniform = new Float32Array(UNIFORM_CURVE_KEY_NUM);
this.updateKeyUniform();
}
}
evaluate (time) {
time = repeat(time, 1);
for (let i = 1; i < this.optimizedKeys.length; i++) {
if (time < this.optimizedKeys[i].time) {
return this.optimizedKeys[i - 1].evaluate(time);
}
}
return this.optimizedKeys[this.optimizedKeys.length - 1].evaluate(time);
}
// calculate first order integral coefficients of all keys
integrateOnce () {
let i = 0;
if (this.integral.length + 1 < this.optimizedKeys.length) {
for (i = 0; i < this.optimizedKeys.length - this.integral.length - 1; i++) {
this.integral.push(0);
}
} else {
this.integral.splice(this.optimizedKeys.length - 1);
}
for (i = 0; i < this.integral.length; i++) {
integrateKeyframe(this.optimizedKeys[i].coefficient);
const deltaT = this.optimizedKeys[i + 1].time - this.optimizedKeys[i].time;
const prevIntegral = i === 0 ? 0 : this.integral[i - 1];
this.integral[i] = prevIntegral + (deltaT * evalOptCurve(deltaT, this.optimizedKeys[i].coefficient));
}
integrateKeyframe(this.optimizedKeys[this.optimizedKeys.length - 1].coefficient);
if (this.constructUniform) {
this.updateKeyUniform();
this.updateIntegralUniform();
}
}
// get the integral of the curve using calculated coefficients
evaluateIntegral (t, ts = 1) {
t = repeat(t, 1);
for (let i = 1; i < this.optimizedKeys.length; i++) {
if (t < this.optimizedKeys[i].time) {
const prevInt = i === 1 ? 0 : this.integral[i - 2];
const dt = t - this.optimizedKeys[i - 1].time;
return ts * (prevInt + (dt * evalOptCurve(dt, this.optimizedKeys[i - 1].coefficient)));
}
}
const dt = t - this.optimizedKeys[this.optimizedKeys.length - 1].time;
return ts * (this.integral[this.integral.length - 1] + (dt * evalOptCurve(dt, this.optimizedKeys[this.optimizedKeys.length - 1].coefficient)));
}
// calculate second order integral coefficients of all keys
integrateTwice () {
let i = 0;
if (this.integral.length + 1 < this.optimizedKeys.length) {
for (i = 0; i < this.optimizedKeys.length - this.integral.length - 1; i++) {
this.integral.push(0);
}
} else {
this.integral.splice(this.optimizedKeys.length - 1);
}
for (i = 0; i < this.integral.length; i++) {
integrateKeyframeTwice(this.optimizedKeys[i].coefficient);
const deltaT = this.optimizedKeys[i + 1].time - this.optimizedKeys[i].time;
const prevIntegral = i === 0 ? 0 : this.integral[i - 1];
this.integral[i] = prevIntegral + (deltaT * deltaT * evalOptCurve(deltaT, this.optimizedKeys[i].coefficient));
}
integrateKeyframeTwice(this.optimizedKeys[this.optimizedKeys.length - 1].coefficient);
if (this.constructUniform) {
this.updateKeyUniform();
this.updateIntegralUniform();
}
}
// get the second order integral of the curve using calculated coefficients
evaluateIntegralTwice (t, ts = 1) {
t = repeat(t, 1);
for (let i = 1; i < this.optimizedKeys.length; i++) {
if (t < this.optimizedKeys[i].time) {
const prevInt = i === 1 ? 0 : this.integral[i - 2];
const dt = t - this.optimizedKeys[i - 1].time;
return ts * ts * (prevInt + (dt * dt * evalOptCurve(dt, this.optimizedKeys[i - 1].coefficient)));
}
}
const dt = t - this.optimizedKeys[this.optimizedKeys.length - 1].time;
return ts * ts * (this.integral[this.integral.length - 1] + (dt * dt * evalOptCurve(dt, this.optimizedKeys[this.optimizedKeys.length - 1].coefficient)));
}
updateKeyUniform () {
if (this.coefUniform != null && this.timeUniform != null) {
for (let i = 0; i < this.optimizedKeys.length; i++) {
this.coefUniform[i * 4] = this.optimizedKeys[i].coefficient[0];
this.coefUniform[i * 4 + 1] = this.optimizedKeys[i].coefficient[1];
this.coefUniform[i * 4 + 2] = this.optimizedKeys[i].coefficient[2];
this.coefUniform[i * 4 + 3] = this.optimizedKeys[i].coefficient[3];
this.timeUniform[i] = this.optimizedKeys[i].endTime;
}
}
}
updateIntegralUniform () {
this.integralUniform = new Float32Array(UNIFORM_CURVE_KEY_NUM - 1);
for (let i = 0; i < this.integral.length; i++) {
this.integralUniform[i] = this.integral[i];
}
}
}

View File

@@ -0,0 +1,99 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import { pseudoRandom } from '../../../value-types';
import CurveRange from './curve-range';
// tslint:disable: max-line-length
const ROTATION_OVERTIME_RAND_OFFSET = 125292;
/**
* !#en The rotation module of 3d particle.
* !#zh 3D 粒子的旋转模块
* @class RotationOvertimeModule
*/
@ccclass('cc.RotationOvertimeModule')
export default class RotationOvertimeModule {
/**
* !#en The enable of RotationOvertimeModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
enable = false;
@property
_separateAxes = false;
/**
* !#en Whether to set the rotation of three axes separately (not currently supported)
* !#zh 是否三个轴分开设定旋转(暂不支持)。
* @property {Boolean} separateAxes
*/
@property
get separateAxes () {
return this._separateAxes;
}
set separateAxes (val) {
this._separateAxes = val;
}
/**
* !#en Set rotation around X axis.
* !#zh 绕 X 轴设定旋转。
* @property {CurveRange} x
*/
@property({
type: CurveRange,
range: [-1, 1],
radian: true,
visible: function (this) {
return this._separateAxes;
}
})
x = new CurveRange();
/**
* !#en Set rotation around Y axis.
* !#zh 绕 Y 轴设定旋转。
* @property {CurveRange} y
*/
@property({
type: CurveRange,
range: [-1, 1],
radian: true,
visible: function (this) {
return this._separateAxes;
}
})
y = new CurveRange();
/**
* !#en Set rotation around Z axis.
* !#zh 绕 Z 轴设定旋转。
* @property {CurveRange} z
*/
@property({
type: CurveRange,
range: [-1, 1],
radian: true,
})
z = new CurveRange();
constructor () {
}
animate (p, dt) {
const normalizedTime = 1 - p.remainingLifetime / p.startLifetime;
if (!this._separateAxes) {
p.rotation.x += this.z.evaluate(normalizedTime, pseudoRandom(p.randomSeed + ROTATION_OVERTIME_RAND_OFFSET)) * dt;
} else {
// TODO: separateAxes is temporarily not supported!
const rotationRand = pseudoRandom(p.randomSeed + ROTATION_OVERTIME_RAND_OFFSET);
p.rotation.x += this.x.evaluate(normalizedTime, rotationRand) * dt;
p.rotation.y += this.y.evaluate(normalizedTime, rotationRand) * dt;
p.rotation.z += this.z.evaluate(normalizedTime, rotationRand) * dt;
}
}
}

View File

@@ -0,0 +1,95 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import { pseudoRandom, Vec3 } from '../../../value-types';
import CurveRange from './curve-range';
// tslint:disable: max-line-length
const SIZE_OVERTIME_RAND_OFFSET = 39825;
/**
* !#en The size module of 3d particle.
* !#zh 3D 粒子的大小模块
* @class SizeOvertimeModule
*/
@ccclass('cc.SizeOvertimeModule')
export default class SizeOvertimeModule {
/**
* !#en The enable of SizeOvertimeModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
enable = false;
/**
* !#en Decide whether to control particle size independently on each axis.
* !#zh 决定是否在每个轴上独立控制粒子大小。
* @property {Boolean} separateAxes
*/
@property
separateAxes = false;
/**
* !#en Define a curve to determine the size change of particles during their life cycle.
* !#zh 定义一条曲线来决定粒子在其生命周期中的大小变化。
* @property {CurveRange} size
*/
@property({
type: CurveRange,
visible: function (this) {
return !this.separateAxes;
}
})
size = new CurveRange();
/**
* !#en Defines a curve to determine the size change of particles in the X-axis direction during their life cycle.
* !#zh 定义一条曲线来决定粒子在其生命周期中 X 轴方向上的大小变化。
* @property {CurveRange} x
*/
@property({
type: CurveRange,
visible: function (this) {
return this.separateAxes;
}
})
x = new CurveRange();
/**
* !#en Defines a curve to determine the size change of particles in the Y-axis direction during their life cycle.
* !#zh 定义一条曲线来决定粒子在其生命周期中 Y 轴方向上的大小变化。
* @property {CurveRange} y
*/
@property({
type: CurveRange,
visible: function (this) {
return this.separateAxes;
}
})
y = new CurveRange();
/**
* !#en Defines a curve to determine the size change of particles in the Z-axis direction during their life cycle.
* !#zh 定义一条曲线来决定粒子在其生命周期中 Z 轴方向上的大小变化。
* @property {CurveRange} z
*/
@property({
type: CurveRange,
visible: function (this) {
return this.separateAxes;
}
})
z = new CurveRange();
animate (particle) {
if (!this.separateAxes) {
Vec3.scale(particle.size, particle.startSize, this.size.evaluate(1 - particle.remainingLifetime / particle.startLifetime, pseudoRandom(particle.randomSeed + SIZE_OVERTIME_RAND_OFFSET)));
} else {
const currLifetime = 1 - particle.remainingLifetime / particle.startLifetime;
const sizeRand = pseudoRandom(particle.randomSeed + SIZE_OVERTIME_RAND_OFFSET);
particle.size.x = particle.startSize.x * this.x.evaluate(currLifetime, sizeRand);
particle.size.y = particle.startSize.y * this.y.evaluate(currLifetime, sizeRand);
particle.size.z = particle.startSize.z * this.z.evaluate(currLifetime, sizeRand);
}
}
}

View File

@@ -0,0 +1,245 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import Enum from '../../../platform/CCEnum';
import { lerp, pseudoRandom, repeat } from '../../../value-types';
import CurveRange from './curve-range';
// tslint:disable: max-line-length
const TEXTURE_ANIMATION_RAND_OFFSET = 90794;
/**
* 粒子贴图动画类型
* @enum textureAnimationModule.Mode
*/
const Mode = Enum({
/**
* 网格类型
*/
Grid: 0,
/**
* 精灵类型(暂未支持)
*/
//Sprites: 1,
});
/**
* 贴图动画的播放方式
* @enum textureAnimationModule.Animation
*/
const Animation = Enum({
/**
* 播放贴图中的所有帧
*/
WholeSheet: 0,
/**
* 播放贴图中的其中一行动画
*/
SingleRow: 1,
});
/**
* !#en The texture animation module of 3d particle.
* !#zh 3D 粒子的贴图动画模块
* @class TextureAnimationModule
*/
@ccclass('cc.TextureAnimationModule')
export default class TextureAnimationModule {
@property
_enable = false;
/**
* !#en The enable of TextureAnimationModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
get enable () {
return this._enable;
}
set enable (val) {
this._enable = val;
this.ps._assembler._updateMaterialParams();
}
@property
_mode = Mode.Grid;
/**
* !#en Set the type of particle map animation (only supports Grid mode for the time being)
* !#zh 设定粒子贴图动画的类型(暂只支持 Grid 模式。
* @property {Mode} mode
*/
@property({
type: Mode,
})
get mode () {
return this._mode;
}
set mode (val) {
if (val !== Mode.Grid) {
console.error('particle texture animation\'s sprites is not supported!');
return;
}
}
@property
_numTilesX = 0;
/**
* !#en Animation frames in X direction.
* !#zh X 方向动画帧数。
* @property {Number} numTilesX
*/
@property
get numTilesX () {
return this._numTilesX;
};
set numTilesX (val) {
if (this._numTilesX === val) return;
this._numTilesX = val;
if (this.ps && this.ps._assembler) this.ps._assembler._updateMaterialParams();
}
@property
_numTilesY = 0;
/**
* !#en Animation frames in Y direction.
* !#zh Y 方向动画帧数。
* @property {Number} numTilesY
*/
@property
get numTilesY () {
return this._numTilesY;
}
set numTilesY (val) {
if (this._numTilesY === val) return;
this._numTilesY = val;
if (this.ps && this.ps._assembler) this.ps._assembler._updateMaterialParams();
}
/**
* !#en The way of the animation plays.
* !#zh 动画播放方式。
* @property {Animation} animation
*/
@property({
type: Animation,
})
animation = Animation.WholeSheet;
/**
* !#en Randomly select a line from the animated map to generate the animation. <br>
     * This option only takes effect when the animation playback mode is SingleRow.
* !#zh 随机从动画贴图中选择一行以生成动画。<br>
* 此选项仅在动画播放方式为 SingleRow 时生效。
* @property {Boolean} randomRow
*/
@property
randomRow = false;
/**
* !#en Select specific lines from the animation map to generate the animation. <br>
     * This option is only available when the animation playback mode is SingleRow and randomRow is disabled.
* !#zh 从动画贴图中选择特定行以生成动画。<br>
* 此选项仅在动画播放方式为 SingleRow 时且禁用 randomRow 时可用。
* @property {Number} rowIndex
*/
@property
rowIndex = 0;
/**
* !#en Frame and time curve of animation playback in one cycle.
* !#zh 一个周期内动画播放的帧与时间变化曲线。
* @property {CurveRange} frameOverTime
*/
@property({
type: CurveRange,
})
frameOverTime = new CurveRange();
/**
* !#en Play from which frames, the time is the life cycle of the entire particle system.
* !#zh 从第几帧开始播放,时间为整个粒子系统的生命周期。
* @property {CurveRange} startFrame
*/
@property({
type: CurveRange,
})
startFrame = new CurveRange();
/**
* !#en Number of playback loops in a life cycle.
* !#zh 一个生命周期内播放循环的次数。
* @property {Number} cycleCount
*/
@property
cycleCount = 0;
_flipU = 0;
@property
get flipU () {
return this._flipU;
}
set flipU (val) {
console.error('particle texture animation\'s flipU is not supported!');
}
_flipV = 0;
@property
get flipV () {
return this._flipV;
}
set flipV (val) {
console.error('particle texture animation\'s flipV is not supported!');
}
_uvChannelMask = -1;
@property
get uvChannelMask () {
return this._uvChannelMask;
}
set uvChannelMask (val) {
console.error('particle texture animation\'s uvChannelMask is not supported!');
}
ps = null;
onInit (ps) {
this.ps = ps;
}
init (p) {
p.startRow = Math.floor(Math.random() * this.numTilesY);
}
animate (p) {
const normalizedTime = 1 - p.remainingLifetime / p.startLifetime;
const startFrame = this.startFrame.evaluate(normalizedTime, pseudoRandom(p.randomSeed + TEXTURE_ANIMATION_RAND_OFFSET)) / (this.numTilesX * this.numTilesY);
if (this.animation === Animation.WholeSheet) {
p.frameIndex = repeat(this.cycleCount * (this.frameOverTime.evaluate(normalizedTime, pseudoRandom(p.randomSeed + TEXTURE_ANIMATION_RAND_OFFSET)) + startFrame), 1);
} else if (this.animation === Animation.SingleRow) {
const rowLength = 1 / this.numTilesY;
if (this.randomRow) {
const f = repeat(this.cycleCount * (this.frameOverTime.evaluate(normalizedTime, pseudoRandom(p.randomSeed + TEXTURE_ANIMATION_RAND_OFFSET)) + startFrame), 1);
const from = p.startRow * rowLength;
const to = from + rowLength;
p.frameIndex = lerp(from, to, f);
} else {
const from = this.rowIndex * rowLength;
const to = from + rowLength;
p.frameIndex = lerp(from, to, repeat(this.cycleCount * (this.frameOverTime.evaluate(normalizedTime, pseudoRandom(p.randomSeed + TEXTURE_ANIMATION_RAND_OFFSET)) + startFrame), 1));
}
}
}
}

View File

@@ -0,0 +1,106 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import { pseudoRandom, Quat, Vec3 } from '../../../value-types';
import { Space } from '../enum';
import { calculateTransform } from '../particle-general-function';
import CurveRange from './curve-range';
// tslint:disable: max-line-length
const VELOCITY_OVERTIME_RAND_OFFSET = 197866;
const _temp_v3 = cc.v3();
/**
* !#en The velocity module of 3d particle.
* !#zh 3D 粒子的速度模块
* @class VelocityOvertimeModule
*/
@ccclass('cc.VelocityOvertimeModule')
export default class VelocityOvertimeModule {
/**
* !#en The enable of VelocityOvertimeModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
enable = false;
/**
* !#en Coordinate system used in speed calculation.
* !#zh 速度计算时采用的坐标系。
* @property {Space} space
*/
@property({
type: Space,
})
space = Space.Local;
/**
* !#en Velocity component in X axis direction
* !#zh X 轴方向上的速度分量。
* @property {CurveRange} x
*/
@property({
type: CurveRange,
range: [-1, 1],
})
x = new CurveRange();
/**
* !#en Velocity component in Y axis direction
* !#zh Y 轴方向上的速度分量。
* @property {CurveRange} y
*/
@property({
type: CurveRange,
range: [-1, 1],
})
y = new CurveRange();
/**
* !#en Velocity component in Z axis direction
* !#zh Z 轴方向上的速度分量。
* @property {CurveRange} z
*/
@property({
type: CurveRange,
range: [-1, 1],
})
z = new CurveRange();
/**
* !#en Speed correction factor (only supports CPU particles).
* !#zh 速度修正系数(只支持 CPU 粒子)。
* @property {CurveRange} speedModifier
*/
@property({
type: CurveRange,
range: [-1, 1],
})
speedModifier = new CurveRange();
rotation = null;
needTransform = false;
constructor () {
this.rotation = new Quat();
this.speedModifier.constant = 1;
this.needTransform = false;
}
update (space, worldTransform) {
this.needTransform = calculateTransform(space, this.space, worldTransform, this.rotation);
}
animate (p) {
const normalizedTime = 1 - p.remainingLifetime / p.startLifetime;
const vel = Vec3.set(_temp_v3, this.x.evaluate(normalizedTime, pseudoRandom(p.randomSeed + VELOCITY_OVERTIME_RAND_OFFSET)), this.y.evaluate(normalizedTime, pseudoRandom(p.randomSeed + VELOCITY_OVERTIME_RAND_OFFSET)), this.z.evaluate(normalizedTime, pseudoRandom(p.randomSeed + VELOCITY_OVERTIME_RAND_OFFSET)));
if (this.needTransform) {
Vec3.transformQuat(vel, vel, this.rotation);
}
Vec3.add(p.animatedVelocity, p.animatedVelocity, vel);
Vec3.add(p.ultimateVelocity, p.velocity, p.animatedVelocity);
Vec3.scale(p.ultimateVelocity, p.ultimateVelocity, this.speedModifier.evaluate(1 - p.remainingLifetime / p.startLifetime, pseudoRandom(p.randomSeed + VELOCITY_OVERTIME_RAND_OFFSET)));
}
}

View File

@@ -0,0 +1,111 @@
import { ccclass, property } from '../../platform/CCClassDecorator';
import { repeat } from '../../value-types';
import CurveRange from './animator/curve-range';
/**
* !#en The burst of 3d particle.
* !#zh 3D 粒子发射时的爆发个数
* @class Burst
*/
@ccclass('cc.Burst')
export default class Burst {
@property
_time = 0;
/**
* !#en Time between the start of the particle system and the trigger of this Brust
* !#zh 粒子系统开始运行到触发此次 Brust 的时间
* @property {Number} time
*/
@property
get time () {
return this._time;
}
set time (val) {
this._time = val;
this._curTime = val;
}
/**
* !#en Minimum number of emitted particles
* !#zh 发射粒子的最小数量
* @property {Number} minCount
*/
@property
minCount = 30;
/**
* !#en Maximum number of emitted particles
* !#zh 发射粒子的最大数量
* @property {Number} maxCount
*/
@property
maxCount = 30;
@property
_repeatCount = 1;
/**
* !#en The number of times Burst was triggered.
* !#zh Burst 的触发次数
* @property {Number} repeatCount
*/
@property
get repeatCount () {
return this._repeatCount;
}
set repeatCount (val) {
this._repeatCount = val;
this._remainingCount = val;
}
/**
* !#en Interval of each trigger
* !#zh 每次触发的间隔时间
* @property {Number} repeatInterval
*/
@property
repeatInterval = 1;
/**
* !#en Number of particles emitted
* !#zh 发射的粒子的数量
* @property {CurveRange} count
*/
@property({
type: CurveRange,
})
count = new CurveRange();
_remainingCount = 0;
_curTime = 0;
constructor () {
this._remainingCount = 0;
this._curTime = 0.0;
}
update (psys, dt) {
if (this._remainingCount === 0) {
this._remainingCount = this._repeatCount;
this._curTime = this._time;
}
if (this._remainingCount > 0) {
let preFrameTime = repeat(psys._time - psys.startDelay.evaluate(0, 1), psys.duration) - dt;
preFrameTime = (preFrameTime > 0.0) ? preFrameTime : 0.0;
const curFrameTime = repeat(psys.time - psys.startDelay.evaluate(0, 1), psys.duration);
if (this._curTime >= preFrameTime && this._curTime < curFrameTime) {
psys.emit(this.count.evaluate(this._curTime / psys.duration, 1), dt - (curFrameTime - this._curTime));
this._curTime += this.repeatInterval;
--this._remainingCount;
}
}
}
getMaxCount (psys) {
return this.count.getMax() * Math.min(Math.ceil(psys.duration / this.repeatInterval), this.repeatCount);
}
}

View File

@@ -0,0 +1,311 @@
import Enum from '../../platform/CCEnum';
import { clamp, inverseLerp, pingPong, repeat } from '../../value-types';
import { ccclass , property} from '../../platform/CCClassDecorator';
const LOOK_FORWARD = 3;
/**
* !#en The wrap mode
* !#zh 循环模式
* @static
* @enum AnimationCurve.WrapMode
*/
const WrapMode = Enum({
/**
* !#en Default
* !#zh 默认模式
* @property Default
* @readonly
* @type {Number}
*/
Default: 0,
/**
* !#en Once Mode
* !#zh Once 模式
* @property Once
* @readonly
* @type {Number}
*/
Once: 1,
/**
* !#en Loop Mode
* !#zh Loop 模式
* @property Loop
* @readonly
* @type {Number}
*/
Loop: 2,
/**
* !#en PingPong Mode
* !#zh PingPong 模式
* @property PingPong
* @readonly
* @type {Number}
*/
PingPong: 3,
/**
* !#en ClampForever Mode
* !#zh ClampForever 模式
* @property ClampForever
* @readonly
* @type {Number}
*/
ClampForever: 4,
});
@ccclass('cc.Keyframe')
export class Keyframe {
/**
* !#en Time.
* !#zh 时间。
* @property {Number} time
*/
@property
time = 0;
/**
* !#en Key value.
* !#zh 关键值。
* @property {Number} value
*/
@property
value = 0;
/**
* !#en In tangent value.
* !#zh 左切值。
* @property {Number} inTangent
*/
@property
inTangent = 0;
/**
* !#en Out tangent value.
* !#zh 右切值。
* @property {Number} outTangent
*/
@property
outTangent = 0;
constructor (time, value, inTangent, outTangent) {
this.time = time || 0;
this.value = value || 0;
this.inTangent = inTangent || 0;
this.outTangent = outTangent || 0;
}
}
export class OptimizedKey {
index = 0;
time = 0;
endTime = 0;
coefficient = null;
constructor () {
this.index = -1;
this.time = 0;
this.endTime = 0;
this.coefficient = new Float32Array(4);
}
evaluate (T) {
const t = T - this.time;
return evalOptCurve(t, this.coefficient);
}
}
export function evalOptCurve (t, coefs) {
return (t * (t * (t * coefs[0] + coefs[1]) + coefs[2])) + coefs[3];
}
const defaultKFStart = new Keyframe(0, 1, 0, 0);
const defaultKFEnd = new Keyframe(1, 1, 0, 0);
/**
* !#en The animation curve of 3d particle.
* !#zh 3D 粒子动画曲线
* @class AnimationCurve
*/
@ccclass('cc.AnimationCurve')
export class AnimationCurve {
/**
* !#en Array of key value.
* !#zh 关键值列表。
* @property {[Keyframe]} keyFrames
*/
@property({
type: [Keyframe],
})
keyFrames = new Array();
/**
* !#en Pre-wrap mode.
* !#zh 前置循环模式。
* @property {WrapMode} preWrapMode
*/
@property({
type: cc.Enum(WrapMode),
visible: false,
})
preWrapMode = WrapMode.ClampForever;
/**
* !#en Post-wrap mode.
* !#zh 后置循环模式。
* @property {WrapMode} postWrapMode
*/
@property({
type: cc.Enum(WrapMode),
visible: false,
})
postWrapMode = WrapMode.ClampForever;
cachedKey = null;
constructor (keyFrames = null) {
if (keyFrames) {
this.keyFrames = keyFrames
} else {
this.keyFrames.push(defaultKFStart);
this.keyFrames.push(defaultKFEnd);
}
this.cachedKey = new OptimizedKey();
}
addKey (keyFrame) {
if (this.keyFrames == null) {
this.keyFrames = [];
}
this.keyFrames.push(keyFrame);
}
// cubic Hermite spline
evaluate_slow (time) {
let wrappedTime = time;
const wrapMode = time < 0 ? this.preWrapMode : this.postWrapMode;
const startTime = this.keyFrames[0].time;
const endTime = this.keyFrames[this.keyFrames.length - 1].time;
switch (wrapMode) {
case WrapMode.Loop:
wrappedTime = repeat(time - startTime, endTime - startTime) + startTime;
break;
case WrapMode.PingPong:
wrappedTime = pingPong(time - startTime, endTime - startTime) + startTime;
break;
case WrapMode.ClampForever:
wrappedTime = clamp(time, startTime, endTime);
break;
}
let preKFIndex = 0;
if (wrappedTime > this.keyFrames[0].time) {
if (wrappedTime >= this.keyFrames[this.keyFrames.length - 1].time) {
preKFIndex = this.keyFrames.length - 2;
}
else {
for (let i = 0; i < this.keyFrames.length - 1; i++) {
if (wrappedTime >= this.keyFrames[0].time && wrappedTime <= this.keyFrames[i + 1].time) {
preKFIndex = i;
break;
}
}
}
}
const keyframe0 = this.keyFrames[preKFIndex];
const keyframe1 = this.keyFrames[preKFIndex + 1];
const t = inverseLerp(keyframe0.time, keyframe1.time, wrappedTime);
const dt = keyframe1.time - keyframe0.time;
const m0 = keyframe0.outTangent * dt;
const m1 = keyframe1.inTangent * dt;
const t2 = t * t;
const t3 = t2 * t;
const a = 2 * t3 - 3 * t2 + 1;
const b = t3 - 2 * t2 + t;
const c = t3 - t2;
const d = -2 * t3 + 3 * t2;
return a * keyframe0.value + b * m0 + c * m1 + d * keyframe1.value;
}
evaluate (time) {
let wrappedTime = time;
const wrapMode = time < 0 ? this.preWrapMode : this.postWrapMode;
const startTime = this.keyFrames[0].time;
const endTime = this.keyFrames[this.keyFrames.length - 1].time;
switch (wrapMode) {
case WrapMode.Loop:
wrappedTime = repeat(time - startTime, endTime - startTime) + startTime;
break;
case WrapMode.PingPong:
wrappedTime = pingPong(time - startTime, endTime - startTime) + startTime;
break;
case WrapMode.ClampForever:
wrappedTime = clamp(time, startTime, endTime);
break;
}
if (!CC_EDITOR) {
if (wrappedTime >= this.cachedKey.time && wrappedTime < this.cachedKey.endTime) {
return this.cachedKey.evaluate(wrappedTime);
}
}
const leftIndex = this.findIndex(this.cachedKey, wrappedTime);
const rightIndex = Math.min(leftIndex + 1, this.keyFrames!.length - 1);
this.calcOptimizedKey(this.cachedKey, leftIndex, rightIndex);
return this.cachedKey.evaluate(wrappedTime);
}
calcOptimizedKey (optKey, leftIndex, rightIndex) {
const lhs = this.keyFrames[leftIndex];
const rhs = this.keyFrames[rightIndex];
optKey.index = leftIndex;
optKey.time = lhs.time;
optKey.endTime = rhs.time;
const dx = rhs.time - lhs.time;
const dy = rhs.value - lhs.value;
const length = 1 / (dx * dx);
const d1 = lhs.outTangent * dx;
const d2 = rhs.inTangent * dx;
optKey.coefficient[0] = (d1 + d2 - dy - dy) * length / dx;
optKey.coefficient[1] = (dy + dy + dy - d1 - d1 - d2) * length;
optKey.coefficient[2] = lhs.outTangent;
optKey.coefficient[3] = lhs.value;
}
findIndex (optKey, t) {
const cachedIndex = optKey.index;
if (cachedIndex !== -1) {
const cachedTime = this.keyFrames![cachedIndex].time;
if (t > cachedTime) {
for (let i = 0; i < LOOK_FORWARD; i++) {
const currIndex = cachedIndex + i;
if (currIndex + 1 < this.keyFrames!.length && this.keyFrames![currIndex + 1].time > t) {
return currIndex;
}
}
} else {
for (let i = 0; i < LOOK_FORWARD; i++) {
const currIndex = cachedIndex - i;
if ((currIndex - 1) >= 0 && this.keyFrames![currIndex - 1].time <= t) {
return currIndex - 1;
}
}
}
}
let left = 0;
let right = this.keyFrames!.length;
let mid;
while (right - left > 1) {
mid = Math.floor((left + right) / 2);
if (this.keyFrames![mid].time >= t) {
right = mid;
} else {
left = mid;
}
}
return left;
}
}
cc.Keyframe = Keyframe;
cc.AnimationCurve = AnimationCurve;

View File

@@ -0,0 +1,456 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import { clamp, Mat4, pingPong, Quat, random, randomRange, repeat, toDegree, toRadian, Vec2, Vec3 } from '../../../value-types';
import CurveRange from '../animator/curve-range';
import { fixedAngleUnitVector2, particleEmitZAxis, randomPointBetweenCircleAtFixedAngle, randomPointBetweenSphere, randomPointInCube, randomSign, randomSortArray, randomUnitVector } from '../particle-general-function';
import { ShapeType, EmitLocation, ArcMode } from '../enum';
// tslint:disable: max-line-length
const _intermediVec = new Vec3(0, 0, 0);
const _intermediArr = new Array();
const _unitBoxExtent = new Vec3(0.5, 0.5, 0.5);
/**
* !#en The shape module of 3d particle.
* !#zh 3D 粒子的发射形状模块
* @class ShapeModule
*/
@ccclass('cc.ShapeModule')
export default class ShapeModule {
/**
* !#en The enable of shapeModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
enable = false;
@property
_shapeType = ShapeType.Cone;
/**
* !#en Particle emitter type.
* !#zh 粒子发射器类型。
* @property {ShapeType} shapeType
*/
@property({
type: ShapeType,
})
public get shapeType () {
return this._shapeType;
}
public set shapeType (val) {
this._shapeType = val;
switch (this._shapeType) {
case ShapeType.Box:
if (this.emitFrom === EmitLocation.Base) {
this.emitFrom = EmitLocation.Volume;
}
break;
case ShapeType.Cone:
if (this.emitFrom === EmitLocation.Edge) {
this.emitFrom = EmitLocation.Base;
}
break;
case ShapeType.Sphere:
case ShapeType.Hemisphere:
if (this.emitFrom === EmitLocation.Base || this.emitFrom === EmitLocation.Edge) {
this.emitFrom = EmitLocation.Volume;
}
break;
}
}
/**
* !#en The emission site of the particle.
* !#zh 粒子从发射器哪个部位发射。
* @property {EmitLocation} emitFrom
*/
@property({
type: EmitLocation,
})
emitFrom = EmitLocation.Volume;
/**
* !#en Particle emitter radius.
* !#zh 粒子发射器半径。
* @property {Number} radius
*/
@property
radius = 1;
/**
* !#en Particle emitter emission position (not valid for Box type emitters)<bg>
* - 0 means emitted from the surface;
     * - 1 means launch from the center;
     * - 0 ~ 1 indicates emission from the center to the surface.
* !#zh 粒子发射器发射位置(对 Box 类型的发射器无效):<bg>
* - 0 表示从表面发射;
* - 1 表示从中心发射;
* - 0 ~ 1 之间表示在中心到表面之间发射。
* @property {Number} radiusThickness
*/
@property
radiusThickness = 1;
@property
_angle = toRadian(25);
/**
* !#en The angle between the axis of the cone and the generatrix<bg>
* Determines the opening and closing of the cone launcher
* !#zh 圆锥的轴与母线的夹角<bg>。
* 决定圆锥发射器的开合程度。
* @property {Number} angle
*/
@property
get angle () {
return Math.round(toDegree(this._angle) * 100) / 100;
}
set angle (val) {
this._angle = toRadian(val);
}
@property
_arc = toRadian(360);
/**
* !#en Particle emitters emit in a fan-shaped range.
* !#zh 粒子发射器在一个扇形范围内发射。
* @property {Number} arc
*/
@property
get arc () {
return toDegree(this._arc);
}
set arc (val) {
this._arc = toRadian(val);
}
/**
* !#en How particles are emitted in the sector range.
* !#zh 粒子在扇形范围内的发射方式。
* @property {ArcMode} arcMode
*/
@property({
type: ArcMode,
})
arcMode = ArcMode.Random;
/**
* !#en Controls the discrete intervals around the arcs where particles might be generated.
* !#zh 控制可能产生粒子的弧周围的离散间隔。
* @property {Number} arcSpread
*/
@property
arcSpread = 0;
/**
* !#en The speed at which particles are emitted around the circumference.
* !#zh 粒子沿圆周发射的速度。
* @property {CurveRange} arcSpeed
*/
@property({
type: CurveRange,
})
arcSpeed = new CurveRange();
/**
* !#en Axis length from top of cone to bottom of cone <bg>.
     * Determines the height of the cone emitter.
* !#zh 圆锥顶部截面距离底部的轴长<bg>。
* 决定圆锥发射器的高度。
* @property {Number} length
*/
@property
length = 5;
/**
* !#en Particle emitter emission location (for box-type particle emitters).
* !#zh 粒子发射器发射位置(针对 Box 类型的粒子发射器。
* @property {Vec3} boxThickness
*/
@property
boxThickness = new Vec3(0, 0, 0);
@property
_position = new Vec3(0, 0, 0);
/**
* !#en Particle Emitter Position
* !#zh 粒子发射器位置。
* @property {Vec3} position
*/
@property
get position () {
return this._position;
}
set position (val) {
this._position = val;
this.constructMat();
}
@property
_rotation = new Vec3(0, 0, 0);
/**
* !#en Particle emitter rotation angle.
* !#zh 粒子发射器旋转角度。
* @property {Vec3} rotation
*/
@property
get rotation () {
return this._rotation;
}
set rotation (val) {
this._rotation = val;
this.constructMat();
}
@property
_scale = new Vec3(1, 1, 1);
/**
* !#en Particle emitter scaling
* !#zh 粒子发射器缩放比例。
* @property {Vec3} scale
*/
@property
get scale () {
return this._scale;
}
set scale (val) {
this._scale = val;
this.constructMat();
}
/**
* !#en The direction of particle movement is determined based on the initial direction of the particles.
* !#zh 根据粒子的初始方向决定粒子的移动方向。
* @property {Boolean} alignToDirection
*/
@property
alignToDirection = false;
/**
* !#en Set particle generation direction randomly.
* !#zh 粒子生成方向随机设定。
* @property {Number} randomDirectionAmount
*/
@property
randomDirectionAmount = 0;
/**
* !#en Interpolation between the current emission direction and the direction from the current position to the center of the node.
* !#zh 表示当前发射方向与当前位置到结点中心连线方向的插值。
* @property {Number} sphericalDirectionAmount
*/
@property
sphericalDirectionAmount = 0;
/**
* !#en Set the particle generation position randomly (setting this value to a value other than 0 will cause the particle generation position to exceed the generator size range)
* !#zh 粒子生成位置随机设定(设定此值为非 0 会使粒子生成位置超出生成器大小范围)。
*/
@property
randomPositionAmount = 0;
mat = null;
Quat = null;
particleSystem = null;
lastTime = null;
totalAngle = null;
constructor () {
this.mat = new Mat4();
this.quat = new Quat();
this.particleSystem = null;
this.lastTime = 0;
this.totalAngle = 0;
}
onInit (ps) {
this.particleSystem = ps;
this.constructMat();
this.lastTime = this.particleSystem._time;
}
constructMat () {
Quat.fromEuler(this.quat, this._rotation.x, this._rotation.y, this._rotation.z);
Mat4.fromRTS(this.mat, this.quat, this._position, this._scale);
}
emit (p) {
switch (this.shapeType) {
case ShapeType.Box:
boxEmit(this.emitFrom, this.boxThickness, p.position, p.velocity);
break;
case ShapeType.Circle:
circleEmit(this.radius, this.radiusThickness, this.generateArcAngle(), p.position, p.velocity);
break;
case ShapeType.Cone:
coneEmit(this.emitFrom, this.radius, this.radiusThickness, this.generateArcAngle(), this._angle, this.length, p.position, p.velocity);
break;
case ShapeType.Sphere:
sphereEmit(this.emitFrom, this.radius, this.radiusThickness, p.position, p.velocity);
break;
case ShapeType.Hemisphere:
hemisphereEmit(this.emitFrom, this.radius, this.radiusThickness, p.position, p.velocity);
break;
default:
console.warn(this.shapeType + ' shapeType is not supported by ShapeModule.');
}
if (this.randomPositionAmount > 0) {
p.position.x += randomRange(-this.randomPositionAmount, this.randomPositionAmount);
p.position.y += randomRange(-this.randomPositionAmount, this.randomPositionAmount);
p.position.z += randomRange(-this.randomPositionAmount, this.randomPositionAmount);
}
Vec3.transformQuat(p.velocity, p.velocity, this.quat);
Vec3.transformMat4(p.position, p.position, this.mat);
if (this.sphericalDirectionAmount > 0) {
const sphericalVel = Vec3.normalize(_intermediVec, p.position);
Vec3.lerp(p.velocity, p.velocity, sphericalVel, this.sphericalDirectionAmount);
}
this.lastTime = this.particleSystem._time;
}
generateArcAngle () {
if (this.arcMode === ArcMode.Random) {
return randomRange(0, this._arc);
}
let angle = this.totalAngle + 2 * Math.PI * this.arcSpeed.evaluate(this.particleSystem._time, 1) * (this.particleSystem._time - this.lastTime);
this.totalAngle = angle;
if (this.arcSpread !== 0) {
angle = Math.floor(angle / (this._arc * this.arcSpread)) * this._arc * this.arcSpread;
}
switch (this.arcMode) {
case ArcMode.Loop:
return repeat(angle, this._arc);
case ArcMode.PingPong:
return pingPong(angle, this._arc);
}
}
}
function sphereEmit (emitFrom, radius, radiusThickness, pos, dir) {
switch (emitFrom) {
case EmitLocation.Volume:
randomPointBetweenSphere(pos, radius * (1 - radiusThickness), radius);
Vec3.copy(dir, pos);
Vec3.normalize(dir, dir);
break;
case EmitLocation.Shell:
randomUnitVector(pos);
Vec3.scale(pos, pos, radius);
Vec3.copy(dir, pos);
break;
default:
console.warn(emitFrom + ' is not supported for sphere emitter.');
}
}
function hemisphereEmit (emitFrom, radius, radiusThickness, pos, dir) {
switch (emitFrom) {
case EmitLocation.Volume:
randomPointBetweenSphere(pos, radius * (1 - radiusThickness), radius);
if (pos.z > 0) {
pos.z *= -1;
}
Vec3.copy(dir, pos);
Vec3.normalize(dir, dir);
break;
case EmitLocation.Shell:
randomUnitVector(pos);
Vec3.scale(pos, pos, radius);
if (pos.z < 0) {
pos.z *= -1;
}
Vec3.copy(dir, pos);
break;
default:
console.warn(emitFrom + ' is not supported for hemisphere emitter.');
}
}
function coneEmit (emitFrom, radius, radiusThickness, theta, angle, length, pos, dir) {
switch (emitFrom) {
case EmitLocation.Base:
randomPointBetweenCircleAtFixedAngle(pos, radius * (1 - radiusThickness), radius, theta);
Vec2.scale(dir, pos, Math.sin(angle));
dir.z = -Math.cos(angle) * radius;
Vec3.normalize(dir, dir);
pos.z = 0;
break;
case EmitLocation.Shell:
fixedAngleUnitVector2(pos, theta);
Vec2.scale(dir, pos, Math.sin(angle));
dir.z = -Math.cos(angle);
Vec3.normalize(dir, dir);
Vec2.scale(pos, pos, radius);
pos.z = 0;
break;
case EmitLocation.Volume:
randomPointBetweenCircleAtFixedAngle(pos, radius * (1 - radiusThickness), radius, theta);
Vec2.scale(dir, pos, Math.sin(angle));
dir.z = -Math.cos(angle) * radius;
Vec3.normalize(dir, dir);
pos.z = 0;
Vec3.add(pos, pos, Vec3.scale(_intermediVec, dir, length * random() / -dir.z));
break;
default:
console.warn(emitFrom + ' is not supported for cone emitter.');
}
}
function boxEmit (emitFrom, boxThickness, pos, dir) {
switch (emitFrom) {
case EmitLocation.Volume:
randomPointInCube(pos, _unitBoxExtent);
// randomPointBetweenCube(pos, Vec3.multiply(_intermediVec, _unitBoxExtent, boxThickness), _unitBoxExtent);
break;
case EmitLocation.Shell:
_intermediArr.splice(0, _intermediArr.length);
_intermediArr.push(randomRange(-0.5, 0.5));
_intermediArr.push(randomRange(-0.5, 0.5));
_intermediArr.push(randomSign() * 0.5);
randomSortArray(_intermediArr);
applyBoxThickness(_intermediArr, boxThickness);
Vec3.set(pos, _intermediArr[0], _intermediArr[1], _intermediArr[2]);
break;
case EmitLocation.Edge:
_intermediArr.splice(0, _intermediArr.length);
_intermediArr.push(randomRange(-0.5, 0.5));
_intermediArr.push(randomSign() * 0.5);
_intermediArr.push(randomSign() * 0.5);
randomSortArray(_intermediArr);
applyBoxThickness(_intermediArr, boxThickness);
Vec3.set(pos, _intermediArr[0], _intermediArr[1], _intermediArr[2]);
break;
default:
console.warn(emitFrom + ' is not supported for box emitter.');
}
Vec3.copy(dir, particleEmitZAxis);
}
function circleEmit (radius, radiusThickness, theta, pos, dir) {
randomPointBetweenCircleAtFixedAngle(pos, radius * (1 - radiusThickness), radius, theta);
Vec3.normalize(dir, pos);
}
function applyBoxThickness (pos, thickness) {
if (thickness.x > 0) {
pos[0] += 0.5 * randomRange(-thickness.x, thickness.x);
pos[0] = clamp(pos[0], -0.5, 0.5);
}
if (thickness.y > 0) {
pos[1] += 0.5 * randomRange(-thickness.y, thickness.y);
pos[1] = clamp(pos[1], -0.5, 0.5);
}
if (thickness.z > 0) {
pos[2] += 0.5 * randomRange(-thickness.z, thickness.z);
pos[2] = clamp(pos[2], -0.5, 0.5);
}
}

View File

@@ -0,0 +1,166 @@
import Enum from '../../platform/CCEnum';
/**
* @enum ParticleSystem3DAssembler.Space
*/
export const Space = Enum({
World: 0,
Local: 1,
Custom: 2,
});
/**
* 粒子的生成模式
* @enum ParticleSystem3DAssembler.RenderMode
*/
export const RenderMode = Enum({
/**
* 粒子始终面向摄像机
*/
Billboard: 0,
/**
* 粒子始终面向摄像机但会根据参数进行拉伸
*/
StrecthedBillboard: 1,
/**
* 粒子始终与 XZ 平面平行
*/
HorizontalBillboard: 2,
/**
* 粒子始终与 Y 轴平行且朝向摄像机
*/
VerticalBillboard: 3,
/**
* 粒子保持模型本身状态
*/
Mesh: 4,
});
/**
* 粒子发射器类型
* @enum shapeModule.ShapeType
*/
export const ShapeType = Enum({
/**
* 立方体类型粒子发射器
* @property {Number} Box
*/
Box: 0,
/**
* 圆形粒子发射器
* @property {Number} Circle
*/
Circle: 1,
/**
* 圆锥体粒子发射器
* @property {Number} Cone
*/
Cone: 2,
/**
* 球体粒子发射器
* @property {Number} Sphere
*/
Sphere: 3,
/**
* 半球体粒子发射器
* @property {Number} Hemisphere
*/
Hemisphere: 4,
});
/**
* 粒子从发射器的哪个部位发射
* @enum shapeModule.EmitLocation
*/
export const EmitLocation = Enum({
/**
* 基础位置发射(仅对 Circle 类型及 Cone 类型的粒子发射器适用)
* @property {Number} Base
*/
Base: 0,
/**
* 边框位置发射(仅对 Box 类型及 Circle 类型的粒子发射器适用)
* @property {Number} Edge
*/
Edge: 1,
/**
* 表面位置发射(对所有类型的粒子发射器都适用)
* @property {Number} Shell
*/
Shell: 2,
/**
* 内部位置发射(对所有类型的粒子发射器都适用)
* @property {Number} Volume
*/
Volume: 3,
});
/**
* 粒子在扇形区域的发射方式
* @enum shapeModule.ArcMode
*/
export const ArcMode = Enum({
/**
* 随机位置发射
* @property {Number} Random
*/
Random: 0,
/**
* 沿某一方向循环发射,每次循环方向相同
* @property {Number} Loop
*/
Loop: 1,
/**
* 循环发射,每次循环方向相反
* @property {Number} PingPong
*/
PingPong: 2,
});
/**
* 选择如何为粒子系统生成轨迹
* @enum trailModule.TrailMode
*/
export const TrailMode = Enum({
/**
* 粒子模式<bg>
* 创建一种效果,其中每个粒子在其路径中留下固定的轨迹
*/
Particles: 0,
/**
* 带模式<bg>
* 根据其生命周期创建连接每个粒子的轨迹带
*/
Ribbon: 1,
});
/**
* 纹理填充模式
* @enum trailModule.TextureMode
*/
export const TextureMode = Enum({
/**
* 拉伸填充纹理
*/
Stretch: 0,
/**
* 重复填充纹理
*/
Repeat: 1,
});

View File

@@ -0,0 +1,111 @@
import { Mat4, Quat, random, randomRange, randomRangeInt, Vec2, Vec3 } from '../../value-types';
import { sign } from '../../value-types/utils';
import { Space } from './enum';
export const particleEmitZAxis = new Vec3(0, 0, -1);
export function calculateTransform (systemSpace, moduleSpace, worldTransform, outQuat) {
if (moduleSpace !== systemSpace) {
if (systemSpace === Space.World) {
Mat4.getRotation(outQuat, worldTransform);
}
else {
Mat4.invert(worldTransform, worldTransform);
Mat4.getRotation(outQuat, worldTransform);
}
return true;
}
else {
Quat.set(outQuat, 0, 0, 0, 1);
return false;
}
}
export function fixedAngleUnitVector2 (out, theta) {
Vec2.set(out, Math.cos(theta), Math.sin(theta));
}
export function randomUnitVector2 (out) {
const a = randomRange(0, 2 * Math.PI);
const x = Math.cos(a);
const y = Math.sin(a);
Vec2.set(out, x, y);
}
export function randomUnitVector (out) {
const z = randomRange(-1, 1);
const a = randomRange(0, 2 * Math.PI);
const r = Math.sqrt(1 - z * z);
const x = r * Math.cos(a);
const y = r * Math.sin(a);
Vec3.set(out, x, y, z);
}
export function randomPointInUnitSphere (out) {
randomUnitVector(out);
Vec3.scale(out, out, random());
}
export function randomPointBetweenSphere (out, minRadius, maxRadius) {
randomUnitVector(out);
Vec3.scale(out, out, minRadius + (maxRadius - minRadius) * random());
}
export function randomPointInUnitCircle (out) {
randomUnitVector2(out);
out.z = 0;
Vec3.scale(out, out, random());
}
export function randomPointBetweenCircle (out, minRadius, maxRadius) {
randomUnitVector2(out);
out.z = 0;
Vec3.scale(out, out, minRadius + (maxRadius - minRadius) * random());
}
export function randomPointBetweenCircleAtFixedAngle (out, minRadius, maxRadius, theta) {
fixedAngleUnitVector2(out, theta);
out.z = 0;
Vec3.scale(out, out, minRadius + (maxRadius - minRadius) * random());
}
export function randomPointInCube (out, extents) {
Vec3.set(out,
randomRange(-extents.x, extents.x),
randomRange(-extents.y, extents.y),
randomRange(-extents.z, extents.z));
}
export function randomPointBetweenCube (out, minBox, maxBox) {
const subscript = ['x', 'y', 'z'];
const edge = randomRangeInt(0, 3);
for (let i = 0; i < 3; i++) {
if (i === edge) {
out[subscript[i]] = randomRange(-maxBox[subscript[i]], maxBox[subscript[i]]);
continue;
}
const x = random() * 2 - 1;
if (x < 0) {
out[subscript[i]] = -minBox[subscript[i]] + x * (maxBox[subscript[i]] - minBox[subscript[i]]);
}
else {
out[subscript[i]] = minBox[subscript[i]] + x * (maxBox[subscript[i]] - minBox[subscript[i]]);
}
}
}
// FisherYates shuffle
export function randomSortArray (arr) {
for (let i = 0; i < arr.length; i++) {
const transpose = i + randomRangeInt(0, arr.length - i);
const val = arr[transpose];
arr[transpose] = arr[i];
arr[i] = val;
}
}
export function randomSign () {
let sgn = randomRange(-1, 1);
sgn === 0 ? sgn++ : sgn;
return sign(sgn);
}

View File

@@ -0,0 +1,970 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { Mat4, pseudoRandom, Quat, randomRangeInt, Vec2, Vec3 } from '../../value-types';
import { INT_MAX } from '../../value-types/utils';
import Material from '../../assets/material/CCMaterial';
import ColorOverLifetimeModule from './animator/color-overtime';
import CurveRange, { Mode }from './animator/curve-range';
import ForceOvertimeModule from './animator/force-overtime';
import GradientRange from './animator/gradient-range';
import LimitVelocityOvertimeModule from './animator/limit-velocity-overtime';
import RotationOvertimeModule from './animator/rotation-overtime';
import SizeOvertimeModule from './animator/size-overtime';
import TextureAnimationModule from './animator/texture-animation';
import VelocityOvertimeModule from './animator/velocity-overtime';
import Burst from './burst';
import ShapeModule from './emitter/shape-module';
import { RenderMode, Space } from './enum';
import { particleEmitZAxis } from './particle-general-function';
import TrailModule from './renderer/trail';
import Mesh from '../../mesh/CCMesh';
const { ccclass, menu, property, executeInEditMode, executionOrder} = require('../../platform/CCClassDecorator')
const RenderComponent = require('../../components/CCRenderComponent');
const _world_mat = new Mat4();
const _module_props = CC_EDITOR && [
"_colorOverLifetimeModule",
"_shapeModule",
"_sizeOvertimeModule",
"_velocityOvertimeModule",
"_forceOvertimeModule",
"_limitVelocityOvertimeModule",
"_rotationOvertimeModule",
"_textureAnimationModule",
"_trailModule"
]
/**
* !#en The ParticleSystem3D Component.
* !#zh 3D 粒子组件
* @class ParticleSystem3D
* @extends RenderComponent
*/
@ccclass('cc.ParticleSystem3D')
@menu('i18n:MAIN_MENU.component.renderers/ParticleSystem3D')
@executionOrder(99)
@executeInEditMode
export default class ParticleSystem3D extends RenderComponent {
/**
* !#en The run time of particle.
* !#zh 粒子系统运行时间
* @property {Number} duration
*/
@property
duration = 5.0;
@property
_capacity = 100;
/**
* !#en The maximum number of particles that a particle system can generate.
* !#zh 粒子系统能生成的最大粒子数量
* @property {Number} capacity
*/
@property
get capacity () {
return this._capacity;
}
set capacity (val) {
this._capacity = val;
if (this._assembler) {
this._assembler.setCapacity(this._capacity);
}
}
/**
* !#en Whether the particle system loops.
* !#zh 粒子系统是否循环播放
* @property {Boolean} loop
*/
@property
loop = true;
/**
* !#en Whether the particles start playing automatically after loaded.
* !#zh 粒子系统加载后是否自动开始播放
* @property {Boolean} playOnAwake
*/
@property({
animatable: false
})
playOnAwake = true;
@property
_prewarm = false;
/**
* !#en When selected, the particle system will start playing after one round has been played (only effective when loop is enabled).
* !#zh 选中之后,粒子系统会以已播放完一轮之后的状态开始播放(仅当循环播放启用时有效)
* @property {Boolean} prewarm
*/
@property({
animatable: false
})
get prewarm () {
return this._prewarm;
}
set prewarm (val) {
if (val === true && this.loop === false) {
// console.warn('prewarm only works if loop is also enabled.');
}
this._prewarm = val;
}
@property
_simulationSpace = Space.Local;
/**
* !#en The coordinate system in which the particle system is located.<br>
* World coordinates (does not change when the position of other objects changes)<br>
* Local coordinates (moving as the position of the parent node changes)<br>
* Custom coordinates (moving with the position of a custom node)
* !#zh 选择粒子系统所在的坐标系<br>
* 世界坐标(不随其他物体位置改变而变换)<br>
* 局部坐标(跟随父节点位置改变而移动)<br>
* 自定坐标(跟随自定义节点的位置改变而移动)
* @property {Space} simulationSpace
*/
@property({
type: Space,
animatable: false
})
get simulationSpace () {
return this._simulationSpace;
}
set simulationSpace (val) {
if (val !== this._simulationSpace) {
this._simulationSpace = val;
if (this._assembler) {
this._assembler._updateMaterialParams();
this._assembler._updateTrailMaterial();
}
}
}
/**
* !#en Controlling the update speed of the entire particle system.
* !#zh 控制整个粒子系统的更新速度。
* @property {Number} simulationSpeed
*/
@property
simulationSpeed = 1.0;
/**
* !#en Delay particle emission time after particle system starts running.
* !#zh 粒子系统开始运行后,延迟粒子发射的时间。
* @property {CurveRange} startDelay
*/
@property({
type: CurveRange,
})
startDelay = new CurveRange();
/**
* !#en Particle life cycle。
* !#zh 粒子生命周期。
* @property {CurveRange} startLifetime
*/
@property({
type: CurveRange,
})
startLifetime = new CurveRange();
/**
* !#en Particle initial color
* !#zh 粒子初始颜色
* @property {GradientRange} startColor
*/
@property({
type: GradientRange,
})
startColor = new GradientRange();
/**
* !#en Particle scale space
* !#zh 缩放空间
* @property {Space} scaleSpace
*/
@property({
type: Space,
})
scaleSpace = Space.Local;
/**
* !#en Initial particle size
* !#zh 粒子初始大小
* @property {CurveRange} startSize
*/
@property({
type: CurveRange,
})
startSize = new CurveRange();
/**
* !#en Initial particle speed
* !#zh 粒子初始速度
* @property {CurveRange} startSpeed
*/
@property({
type: CurveRange,
range: [-1, 1],
})
startSpeed = new CurveRange();
/**
* !#en Particle initial rotation angle
* !#zh 粒子初始旋转角度
* @property {CurveRange} startRotation
*/
@property({
type: CurveRange,
range: [-1, 1],
radian: true,
})
startRotation = new CurveRange();
/**
* !#en Gravity coefficient of particles affected by gravity
* !#zh 粒子受重力影响的重力系数
* @property {CurveRange} gravityModifier
*/
@property({
type: CurveRange,
range: [-1, 1],
})
gravityModifier = new CurveRange();
// emission module
/**
* !#en Particles emitted per second
* !#zh 每秒发射的粒子数
* @property {CurveRange} rateOverTime
*/
@property({
type: CurveRange,
})
rateOverTime = new CurveRange();
/**
* !#en Number of particles emitted per unit distance moved
* !#zh 每移动单位距离发射的粒子数
* @property {CurveRange} rateOverDistance
*/
@property({
type: CurveRange,
})
rateOverDistance = new CurveRange();
/**
* !#en The number of Brusts that emit a specified number of particles at a specified time
* !#zh 设定在指定时间发射指定数量的粒子的 Brust 的数量
* @property {[Burst]} bursts
*/
@property({
type: [Burst],
animatable: false
})
bursts = new Array();
@property({
type: [Material],
displayName: 'Materials',
visible: false,
override: true,
})
get materials () {
// if we don't create an array copy, the editor will modify the original array directly.
return this._materials;
}
set materials (val) {
this._materials = val;
this._activateMaterial();
}
@property
// shpae module
_shapeModule = new ShapeModule();
/**
* !#en Particle emitter module
* !#zh 粒子发射器模块
* @property {ShapeModule} shapeModule
*/
@property({
type: ShapeModule,
animatable: false
})
get shapeModule () {
return this._shapeModule;
}
set shapeModule (val) {
this._shapeModule = val;
this._shapeModule.onInit(this);
}
@property
// color over lifetime module
_colorOverLifetimeModule = new ColorOverLifetimeModule();
/**
* !#en Color control module
* !#zh 颜色控制模块
* @property {ColorOverLifetimeModule} colorOverLifetimeModule
*/
@property({
type: ColorOverLifetimeModule,
animatable: false
})
get colorOverLifetimeModule () {
return this._colorOverLifetimeModule;
}
set colorOverLifetimeModule (val) {
this._colorOverLifetimeModule = val;
}
@property
// size over lifetime module
_sizeOvertimeModule = new SizeOvertimeModule();
/**
* !#en Particle size module
* !#zh 粒子大小模块
* @property {SizeOvertimeModule} sizeOvertimeModule
*/
@property({
type: SizeOvertimeModule,
animatable: false
})
get sizeOvertimeModule () {
return this._sizeOvertimeModule;
}
set sizeOvertimeModule (val) {
this._sizeOvertimeModule = val;
}
@property
_velocityOvertimeModule = new VelocityOvertimeModule();
/**
* !#en Particle speed module
* !#zh 粒子速度模块
* @property {VelocityOvertimeModule} velocityOvertimeModule
*/
@property({
type: VelocityOvertimeModule,
animatable: false
})
get velocityOvertimeModule () {
return this._velocityOvertimeModule;
}
set velocityOvertimeModule (val) {
this._velocityOvertimeModule = val;
}
@property
_forceOvertimeModule = new ForceOvertimeModule();
/**
* !#en Particle acceleration module
* !#zh 粒子加速度模块
* @property {ForceOvertimeModule} forceOvertimeModule
*/
@property({
type: ForceOvertimeModule,
animatable: false
})
get forceOvertimeModule () {
return this._forceOvertimeModule;
}
set forceOvertimeModule (val) {
this._forceOvertimeModule = val;
}
@property
_limitVelocityOvertimeModule = new LimitVelocityOvertimeModule();
/**
* !#en Particle limit speed module (only CPU particles are supported)
* !#zh 粒子限制速度模块(只支持 CPU 粒子)
* @property {LimitVelocityOvertimeModule} limitVelocityOvertimeModule
*/
@property({
type: LimitVelocityOvertimeModule,
animatable: false
})
get limitVelocityOvertimeModule () {
return this._limitVelocityOvertimeModule;
}
set limitVelocityOvertimeModule (val) {
this._limitVelocityOvertimeModule = val;
}
@property
_rotationOvertimeModule = new RotationOvertimeModule();
/**
* !#en Particle rotation module
* !#zh 粒子旋转模块
* @property {RotationOvertimeModule} rotationOvertimeModule
*/
@property({
type: RotationOvertimeModule,
animatable: false
})
get rotationOvertimeModule () {
return this._rotationOvertimeModule;
}
set rotationOvertimeModule (val) {
this._rotationOvertimeModule = val;
}
@property
_textureAnimationModule = new TextureAnimationModule();
/**
* !#en Texture Animation Module
* !#zh 贴图动画模块
* @property {TextureAnimationModule} textureAnimationModule
*/
@property({
type: TextureAnimationModule,
animatable: false
})
get textureAnimationModule () {
return this._textureAnimationModule;
}
set textureAnimationModule (val) {
this._textureAnimationModule = val;
this._textureAnimationModule.onInit(this);
}
@property
_trailModule = new TrailModule();
/**
* !#en Particle Trajectory Module
* !#zh 粒子轨迹模块
* @property {TrailModule} trailModule
*/
@property({
type: TrailModule,
animatable: false
})
get trailModule () {
return this._trailModule;
}
set trailModule (val) {
this._trailModule = val;
this._trailModule.onInit(this);
}
@property
_renderMode = RenderMode.Billboard;
/**
* !#en Particle generation mode
* !#zh 设定粒子生成模式
* @property {RenderMode} renderMode
*/
@property({
type: RenderMode,
animatable: false
})
get renderMode () {
return this._renderMode;
}
set renderMode (val) {
if (this._renderMode === val) {
return;
}
this._renderMode = val;
if (this._assembler) {
this._assembler._setVertexAttrib();
this._assembler._updateModel();
this._assembler._updateMaterialParams();
}
}
@property
_velocityScale = 1;
/**
* !#en When the particle generation mode is StrecthedBillboard, in the direction of movement of the particles is stretched by velocity magnitude
* !#zh 在粒子生成方式为 StrecthedBillboard 时,对粒子在运动方向上按速度大小进行拉伸
* @property {Number} velocityScale
*/
@property({
animatable: false
})
get velocityScale () {
return this._velocityScale;
}
set velocityScale (val) {
this._velocityScale = val;
this._assembler && this._assembler._updateMaterialParams();
}
@property
_lengthScale = 1;
/**
* !#en When the particle generation method is StrecthedBillboard, the particles are stretched according to the particle size in the direction of motion
* !#zh 在粒子生成方式为 StrecthedBillboard 时,对粒子在运动方向上按粒子大小进行拉伸
* @property {Number} lengthScale
*/
@property({
animatable: false
})
get lengthScale () {
return this._lengthScale;
}
set lengthScale (val) {
this._lengthScale = val;
this._assembler && this._assembler._updateMaterialParams();
}
@property
_mesh = null;
/**
* !#en Particle model
* !#zh 粒子模型
* @property {Mesh} mesh
*/
@property({
type: Mesh,
animatable: false
})
get mesh () {
return this._mesh;
}
set mesh (val) {
this._mesh = val;
this._assembler && this._assembler._updateModel();
}
/**
* !#en Particle material
* !#zh 粒子材质
* @property {Material} particleMaterial
*/
@property({
type: Material,
animatable: false
})
get particleMaterial () {
return this.getMaterial(0);
}
set particleMaterial (val) {
this.setMaterial(0, val);
this._onMaterialModified(0, val);
}
/**
* !#en Particle trail material
* !#zh 粒子轨迹材质
* @property {Material} trailMaterial
*/
@property({
type: Material,
animatable: false
})
get trailMaterial () {
return this.getMaterial(1);
}
set trailMaterial (val) {
this.setMaterial(1, val);
this._onMaterialModified(1, val);
}
_isPlaying;
_isPaused;
_isStopped;
_isEmitting;
_time; // playback position in seconds.
_emitRateTimeCounter;
_emitRateDistanceCounter;
_oldWPos;
_curWPos;
_customData1;
_customData2;
_subEmitters; // array of { emitter: ParticleSystem3D, type: 'birth', 'collision' or 'death'}
constructor () {
super();
this.rateOverTime.constant = 10;
this.startLifetime.constant = 5;
this.startSize.constant = 1;
this.startSpeed.constant = 5;
// internal status
this._isPlaying = false;
this._isPaused = false;
this._isStopped = true;
this._isEmitting = false;
this._time = 0.0; // playback position in seconds.
this._emitRateTimeCounter = 0.0;
this._emitRateDistanceCounter = 0.0;
this._oldWPos = new Vec3(0, 0, 0);
this._curWPos = new Vec3(0, 0, 0);
this._customData1 = new Vec2(0, 0);
this._customData2 = new Vec2(0, 0);
this._subEmitters = []; // array of { emitter: ParticleSystemComponent, type: 'birth', 'collision' or 'death'}
}
onLoad () {
this._assembler.onInit(this);
this.shapeModule.onInit(this);
this.trailModule.onInit(this);
this.textureAnimationModule.onInit(this);
this._resetPosition();
// this._system.add(this);
}
_onMaterialModified (index, material) {
this._assembler && this._assembler._onMaterialModified(index, material);
}
_onRebuildPSO (index, material) {
this._assembler && this._assembler._onRebuildPSO(index, material);
}
// TODO: fastforward current particle system by simulating particles over given period of time, then pause it.
// simulate(time, withChildren, restart, fixedTimeStep) {
// }
/**
* !#en Playing particle effects
* !#zh 播放粒子效果
* @method play
*/
play () {
if (this._isPaused) {
this._isPaused = false;
}
if (this._isStopped) {
this._isStopped = false;
}
this._isPlaying = true;
this._isEmitting = true;
this._resetPosition();
// prewarm
if (this._prewarm) {
this._prewarmSystem();
}
}
/**
* !#en Pause particle effect
* !#zh 暂停播放粒子效果
* @method pause
*/
pause () {
if (this._isStopped) {
console.warn('pause(): particle system is already stopped.');
return;
}
if (this._isPlaying) {
this._isPlaying = false;
}
this._isPaused = true;
}
/**
* !#en Stop particle effect
* !#zh 停止播放粒子效果
* @method stop
*/
stop () {
if (this._isPlaying || this._isPaused) {
this.clear();
}
if (this._isPlaying) {
this._isPlaying = false;
}
if (this._isPaused) {
this._isPaused = false;
}
this._time = 0.0;
this._emitRateTimeCounter = 0.0;
this._emitRateDistanceCounter = 0.0;
this._isStopped = true;
}
// remove all particles from current particle system.
/**
* !#en Remove all particle effect
* !#zh 将所有粒子从粒子系统中清除
* @method clear
*/
clear () {
if (this.enabledInHierarchy) {
this._assembler && this._assembler.clear();
this.trailModule.clear();
}
}
getParticleCount () {
return this._assembler ? this._assembler.getParticleCount() : 0;
}
setCustomData1 (x, y) {
Vec2.set(this._customData1, x, y);
}
setCustomData2 (x, y) {
Vec2.set(this._customData2, x, y);
}
onDestroy () {
// this._system.remove(this);
this._assembler.onDestroy();
this.trailModule.destroy();
}
onEnable () {
super.onEnable();
if (this.playOnAwake) {
this.play();
}
this._assembler.onEnable();
this.trailModule.onEnable();
}
onDisable () {
super.onDisable();
this._assembler.onDisable();
this.trailModule.onDisable();
}
update (dt) {
const scaledDeltaTime = dt * this.simulationSpeed;
if (this._isPlaying) {
this._time += scaledDeltaTime;
// excute emission
this._emit(scaledDeltaTime);
// simulation, update particles.
if (this._assembler._updateParticles(scaledDeltaTime) === 0 && !this._isEmitting) {
this.stop();
}
// update render data
this._assembler.updateParticleBuffer();
// update trail
if (this.trailModule.enable) {
this.trailModule.updateTrailBuffer();
}
}
}
emit (count, dt) {
if (!this._assembler) {
return;
}
if (this._simulationSpace === Space.World) {
this.node.getWorldMatrix(_world_mat);
}
for (let i = 0; i < count; ++i) {
const particle = this._assembler._getFreeParticle();
if (particle === null) {
return;
}
const rand = pseudoRandom(randomRangeInt(0, INT_MAX));
if (this.shapeModule.enable) {
this.shapeModule.emit(particle);
}
else {
Vec3.set(particle.position, 0, 0, 0);
Vec3.copy(particle.velocity, particleEmitZAxis);
}
if (this.textureAnimationModule.enable) {
this.textureAnimationModule.init(particle);
}
Vec3.scale(particle.velocity, particle.velocity, this.startSpeed.evaluate(this._time / this.duration, rand));
switch (this._simulationSpace) {
case Space.Local:
break;
case Space.World:
Vec3.transformMat4(particle.position, particle.position, _world_mat);
const worldRot = new Quat();
this.node.getWorldRotation(worldRot);
Vec3.transformQuat(particle.velocity, particle.velocity, worldRot);
break;
case Space.Custom:
// TODO:
break;
}
Vec3.copy(particle.ultimateVelocity, particle.velocity);
// apply startRotation. now 2D only.
Vec3.set(particle.rotation, 0, 0, this.startRotation.evaluate(this._time / this.duration, rand));
// apply startSize. now 2D only.
Vec3.set(particle.startSize, this.startSize.evaluate(this._time / this.duration, rand), 1, 1);
particle.startSize.y = particle.startSize.x;
Vec3.copy(particle.size, particle.startSize);
// apply startColor.
particle.startColor.set(this.startColor.evaluate(this._time / this.duration, rand));
particle.color.set(particle.startColor);
// apply startLifetime.
particle.startLifetime = this.startLifetime.evaluate(this._time / this.duration, rand) + dt;
particle.remainingLifetime = particle.startLifetime;
particle.randomSeed = randomRangeInt(0, 233280);
this._assembler._setNewParticle(particle);
} // end of particles forLoop.
}
// initialize particle system as though it had already completed a full cycle.
_prewarmSystem () {
this.startDelay.mode = Mode.Constant; // clear startDelay.
this.startDelay.constant = 0;
const dt = 1.0; // should use varying value?
const cnt = this.duration / dt;
for (let i = 0; i < cnt; ++i) {
this._time += dt;
this._emit(dt);
this._assembler && this._assembler._updateParticles(dt);
}
}
// internal function
_emit (dt) {
// emit particles.
const startDelay = this.startDelay.evaluate(0, 1);
if (this._time > startDelay) {
if (this._time > (this.duration + startDelay)) {
// this._time = startDelay; // delay will not be applied from the second loop.(Unity)
// this._emitRateTimeCounter = 0.0;
// this._emitRateDistanceCounter = 0.0;
if (!this.loop) {
this._isEmitting = false;
return;
}
}
// emit by rateOverTime
this._emitRateTimeCounter += this.rateOverTime.evaluate(this._time / this.duration, 1) * dt;
if (this._emitRateTimeCounter > 1 && this._isEmitting) {
const emitNum = Math.floor(this._emitRateTimeCounter);
this._emitRateTimeCounter -= emitNum;
this.emit(emitNum, dt);
}
// emit by rateOverDistance
this.node.getWorldPosition(this._curWPos);
const distance = Vec3.distance(this._curWPos, this._oldWPos);
Vec3.copy(this._oldWPos, this._curWPos);
this._emitRateDistanceCounter += distance * this.rateOverDistance.evaluate(this._time / this.duration, 1);
if (this._emitRateDistanceCounter > 1 && this._isEmitting) {
const emitNum = Math.floor(this._emitRateDistanceCounter);
this._emitRateDistanceCounter -= emitNum;
this.emit(emitNum, dt);
}
// bursts
for (const burst of this.bursts) {
burst.update(this, dt);
}
}
}
_activateMaterial () {
}
_resetPosition () {
this.node.getWorldPosition(this._oldWPos);
Vec3.copy(this._curWPos, this._oldWPos);
}
addSubEmitter (subEmitter) {
this._subEmitters.push(subEmitter);
}
removeSubEmitter (idx) {
this._subEmitters.splice(this._subEmitters.indexOf(idx), 1);
}
addBurst (burst) {
this.bursts.push(burst);
}
removeBurst (idx) {
this.bursts.splice(this.bursts.indexOf(idx), 1);
}
_checkBacth () {
}
get isPlaying () {
return this._isPlaying;
}
get isPaused () {
return this._isPaused;
}
get isStopped () {
return this._isStopped;
}
get isEmitting () {
return this._isEmitting;
}
get time () {
return this._time;
}
}
CC_EDITOR && (ParticleSystem3D.prototype._onBeforeSerialize = function(props){return props.filter(p => !_module_props.includes(p) || this[p].enable);});
cc.ParticleSystem3D = ParticleSystem3D;

View File

@@ -0,0 +1,50 @@
import { Pool } from '../../../renderer/memop';
export class ParticleUtils {
static particleSystemPool = {};
static registeredSceneEvent = false;
/**
* instantiate
*/
static instantiate (prefab) {
if (!this.registeredSceneEvent) {
cc.director.on(cc.Director.EVENT_BEFORE_SCENE_LAUNCH, this.onSceneUnload, this);
this.registeredSceneEvent = true;
}
if (!this.particleSystemPool.has(prefab._uuid)) {
this.particleSystemPool.set(prefab._uuid, new Pool(() => {
return cc.instantiate(prefab);
}, 1));
}
return this.particleSystemPool.get(prefab._uuid).alloc();
}
static destroy (prefab) {
if (this.particleSystemPool.has(prefab._prefab.asset._uuid)) {
this.stop(prefab);
this.particleSystemPool.get(prefab._prefab.asset._uuid).free(prefab);
}
}
static onSceneUnload () {
for (const p of this.particleSystemPool.values()) {
p.clear((prefab) => {
prefab.destroy();
});
}
this.particleSystemPool.clear();
}
static play (rootNode) {
for (const ps of rootNode.getComponentsInChildren(cc.ParticleSystem3D)) {
ps.play();
}
}
static stop (rootNode) {
for (const ps of rootNode.getComponentsInChildren(cc.ParticleSystem3D)) {
ps.stop();
}
}
}

View File

@@ -0,0 +1,43 @@
import { Vec3, Color } from '../../value-types';
export default class Particle {
particleSystem = null;
position = null;
velocity = null;
animatedVelocity = null;
ultimateVelocity = null;
angularVelocity = null;
axisOfRotation = null;
rotation = null;
startSize = null;
size = null;
startColor = null;
color = cc.Color.WHITE;
randomSeed = null; // uint
remainingLifetime = null;
startLifetime = null;
emitAccumulator0 = null;
emitAccumulator1 = null;
frameIndex = null;
constructor (particleSystem) {
this.particleSystem = particleSystem;
this.position = new Vec3(0, 0, 0);
this.velocity = new Vec3(0, 0, 0);
this.animatedVelocity = new Vec3(0, 0, 0);
this.ultimateVelocity = new Vec3(0, 0, 0);
this.angularVelocity = new Vec3(0, 0, 0);
this.axisOfRotation = new Vec3(0, 0, 0);
this.rotation = new Vec3(0, 0, 0);
this.startSize = new Vec3(0, 0, 0);
this.size = new Vec3(0, 0, 0);
this.startColor = cc.Color.WHITE.clone();
this.color = cc.Color.WHITE.clone();
this.randomSeed = 0; // uint
this.remainingLifetime = 0.0;
this.startLifetime = 0.0;
this.emitAccumulator0 = 0.0;
this.emitAccumulator1 = 0.0;
this.frameIndex = 0.0;
}
}

View File

@@ -0,0 +1,296 @@
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
import gfx from '../../../../renderer/gfx'
import InputAssembler from '../../../../renderer/core/input-assembler'
import { MeshData } from '../../../mesh/mesh-data'
const renderer = require('../../../renderer');
export default class ParticleBatchModel{
_capacity = 0;
_vertFormat = null;
_vertAttrsFloatCount = 0;
_mesh = null;
_vertCount = 0;
_indexCount = 0;
_material = null;
constructor () {
this._capacity = 0;
this._vertFormat = null;
this._vertAttrsFloatCount = 0;
this._mesh = null;
this._subDatas = [];
this._subMeshes = [];
}
setCapacity (capacity) {
const capChanged = this._capacity !== capacity;
this._capacity = capacity;
if (this._inited && capChanged) {
this._recreateBuffer();
}
}
setVertexAttributes (mesh, vfmt) {
if (this._mesh === mesh && this._vertFormat === vfmt) {
return;
}
this._mesh = mesh;
this._vertFormat = vfmt;
this._vertAttrsFloatCount = this._vertFormat._bytes / 4; // number of float
// rebuid
this._createParticleData();
this._inited = true;
}
_recreateBuffer () {
this._createParticleData();
}
_createParticleData () {
this.destroyIAData();
this._vertCount = 4;
this._indexCount = 6;
let vbData = null;
let ibData = null;
let vertSize = this._vertFormat._bytes
if (this._mesh) {
let subData = this._mesh._subDatas[0];
this._vertCount = subData.vData.byteLength / subData.vfm._bytes;
this._indexCount = subData.iData.byteLength / 2;
vbData = new Float32Array(vertSize * this._capacity * this._vertCount / 4);
ibData = new Uint16Array(this._capacity * this._indexCount);
let posEle = this._vertFormat.element(gfx.ATTR_TEX_COORD3);
let normalEle = this._vertFormat.element(gfx.ATTR_NORMAL);
let uvEle = this._vertFormat.element(gfx.ATTR_TEX_COORD);
let colorEle = this._vertFormat.element(gfx.ATTR_COLOR1);
this._mesh.copyAttribute(0, gfx.ATTR_POSITION, vbData.buffer, vertSize, posEle.offset);
this._mesh.copyAttribute(0, gfx.ATTR_NORMAL, vbData.buffer, vertSize, normalEle.offset);
this._mesh.copyAttribute(0, gfx.ATTR_UV0, vbData.buffer, vertSize, uvEle.offset);
if (!this._mesh.copyAttribute(0, gfx.ATTR_COLOR, vbData.buffer, vertSize, colorEle.offset)) { // copy mesh color to ATTR_COLOR1
const vb = new Uint32Array(vbData.buffer);
for (var i = 0; i < this._vertCount; ++i) {
vb[i * this._vertAttrsFloatCount + colorEle.offset / 4] = cc.Color.WHITE._val;
}
}
const vbFloatArray = new Float32Array(vbData.buffer);
for (var i = 1; i < this._capacity; i++) {
vbFloatArray.copyWithin(i * vertSize * this._vertCount / 4, 0, vertSize * this._vertCount / 4);
}
this._mesh.copyIndices(0, ibData);
// indices
for (var i = 1; i < this._capacity; i++) {
for (var j = 0; j < this._indexCount; j++) {
ibData[i * this._indexCount + j] = ibData[j] + i * this._vertCount;
}
}
} else {
vbData = new Float32Array(vertSize * this._capacity * this._vertCount / 4);
ibData = new Uint16Array(this._capacity * this._indexCount);
let dst = 0;
for (var i = 0; i < this._capacity; ++i) {
const baseIdx = 4 * i;
ibData[dst++] = baseIdx;
ibData[dst++] = baseIdx + 1;
ibData[dst++] = baseIdx + 2;
ibData[dst++] = baseIdx + 3;
ibData[dst++] = baseIdx + 2;
ibData[dst++] = baseIdx + 1;
}
}
let meshData = new MeshData();
meshData.vData = vbData;
meshData.iData = ibData;
meshData.vfm = this._vertFormat;
meshData.vDirty = true;
meshData.iDirty = true;
meshData.enable = true;
this._subDatas[0] = meshData;
if (CC_JSB && CC_NATIVERENDERER) {
meshData.vDirty = true;
} else {
let vb = new gfx.VertexBuffer(
renderer.device,
this._vertFormat,
gfx.USAGE_DYNAMIC,
vbData
);
let ib = new gfx.IndexBuffer(
renderer.device,
gfx.INDEX_FMT_UINT16,
gfx.USAGE_STATIC,
ibData,
ibData.length
);
this._subMeshes[0] = new InputAssembler(vb, ib);
}
}
createTrailData (vfmt, num) {
if (this._subDatas[1]) {
return
}
let vertSize = vfmt._bytes;
let vBuffer = new ArrayBuffer(vertSize * (num + 1) * 2);
let ibData = new Uint16Array(num * 6);
let meshData = new MeshData();
meshData.vData = new Float32Array(vBuffer);
meshData.iData = ibData;
meshData.vfm = vfmt;
meshData.vDirty = true;
meshData.iDirty = true;
meshData.enable = true;
this._subDatas[1] = meshData;
if (CC_JSB && CC_NATIVERENDERER) {
meshData.vDirty = true;
} else {
let vb = new gfx.VertexBuffer(
renderer.device,
vfmt,
gfx.USAGE_DYNAMIC,
vBuffer
);
let ib = new gfx.IndexBuffer(
renderer.device,
gfx.INDEX_FMT_UINT16,
gfx.USAGE_DYNAMIC,
ibData,
num * 6
);
this._subMeshes[1] = new InputAssembler(vb, ib);
}
}
setModelMaterial (mat) {
this._material = mat;
}
addParticleVertexData (index, pvdata) {
let subData = this._subDatas[0];
let vData = subData.getVData();
let uintVData = subData.getVData(Uint32Array);
if (!this._mesh) {
let offset = index * this._vertAttrsFloatCount;
vData[offset++] = pvdata[0].x; // position
vData[offset++] = pvdata[0].y;
vData[offset++] = pvdata[0].z;
vData[offset++] = pvdata[1].x; // uv
vData[offset++] = pvdata[1].y;
vData[offset++] = pvdata[1].z; // frame idx
vData[offset++] = pvdata[2].x; // size
vData[offset++] = pvdata[2].y;
vData[offset++] = pvdata[2].z;
vData[offset++] = pvdata[3].x; // rotation
vData[offset++] = pvdata[3].y;
vData[offset++] = pvdata[3].z;
uintVData[offset++] = pvdata[4]; // color
if (pvdata[5]) {
vData[offset++] = pvdata[5].x; // velocity
vData[offset++] = pvdata[5].y;
vData[offset++] = pvdata[5].z;
}
} else {
for (let i = 0; i < this._vertCount; i++) {
let offset = (index * this._vertCount + i) * this._vertAttrsFloatCount;
vData[offset++] = pvdata[0].x; // position
vData[offset++] = pvdata[0].y;
vData[offset++] = pvdata[0].z;
offset += 2;
vData[offset++] = pvdata[1].z; // frame idx
vData[offset++] = pvdata[2].x; // size
vData[offset++] = pvdata[2].y;
vData[offset++] = pvdata[2].z;
vData[offset++] = pvdata[3].x; // rotation
vData[offset++] = pvdata[3].y;
vData[offset++] = pvdata[3].z;
uintVData[offset++] = pvdata[4]; // color
}
}
}
_uploadData () {
let subDatas = this._subDatas;
let subMeshes = this._subMeshes;
for (let i = 0, len = subDatas.length; i < len; i++) {
let subData = subDatas[i];
let subMesh = subMeshes[i];
if (subData.vDirty) {
let vBuffer = subMesh._vertexBuffer, vData = subData.vData;
vBuffer.update(0, vData);
subData.vDirty = false;
}
if (subData.iDirty) {
let iBuffer = subMesh._indexBuffer, iData = subData.iData;
iBuffer.update(0, iData);
subData.iDirty = false;
}
}
}
updateIA (index, count, vDirty, iDirty) {
if (CC_JSB && CC_NATIVERENDERER) return
this._subMeshes[index]._count = count;
let subData = this._subDatas[index];
subData.vDirty = vDirty;
subData.iDirty = iDirty;
}
clear () {
let subMesh = this._subMeshes[0];
if (subMesh) {
subMesh.indexCount = 0;
}
}
destroy () {
this._subDatas.length = 0;
let subMeshes = this._subMeshes;
for (let i = 0, len = subMeshes.length; i < len; i++) {
let vb = subMeshes[i]._vertexBuffer;
if (vb) {
vb.destroy();
}
let ib = subMeshes[i]._indexBuffer;
if (ib) {
ib.destroy();
}
}
subMeshes.length = 0;
}
destroyIAData () {
if (this._subMeshes[0]) {
this._subMeshes[0]._vertexBuffer.destroy();
this._subMeshes[0]._indexBuffer.destroy();
this._subMeshes[0] = null;
}
this._subDatas[0] = null;
}
}

View File

@@ -0,0 +1,467 @@
import { Mat4, Vec2, Vec3, Vec4 } from '../../../value-types';
import gfx from '../../../../renderer/gfx';
import ParticleBatchModel from './particle-batch-model';
import MaterialVariant from '../../../assets/material/material-variant';
import RecyclePool from '../../../../renderer/memop/recycle-pool';
import { RenderMode, Space } from '../enum';
import Particle from '../particle';
import Assembler from '../../../renderer/assembler';
import ParticleSystem3D from '../particle-system-3d';
const { ccclass, property } = require('../../../platform/CCClassDecorator');
// tslint:disable: max-line-length
const _tempAttribUV = new Vec3();
const _tempAttribUV0 = new Vec2();
const _tempAttribColor = new Vec4();
const _tempWorldTrans = new Mat4();
const _uvs = [
0, 0, // bottom-left
1, 0, // bottom-right
0, 1, // top-left
1, 1, // top-right
];
const CC_USE_WORLD_SPACE = 'CC_USE_WORLD_SPACE';
const CC_USE_BILLBOARD = 'CC_USE_BILLBOARD';
const CC_USE_STRETCHED_BILLBOARD = 'CC_USE_STRETCHED_BILLBOARD';
const CC_USE_HORIZONTAL_BILLBOARD = 'CC_USE_HORIZONTAL_BILLBOARD';
const CC_USE_VERTICAL_BILLBOARD = 'CC_USE_VERTICAL_BILLBOARD';
const CC_USE_MESH = 'CC_USE_MESH';
//const CC_DRAW_WIRE_FRAME = 'CC_DRAW_WIRE_FRAME'; // <wireframe debug>
var vfmtNormal = new gfx.VertexFormat([
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD1, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD2, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
]);
vfmtNormal.name = 'vfmtNormal';
var vfmtStretch = new gfx.VertexFormat([
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD1, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD2, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
{ name: gfx.ATTR_COLOR1, type: gfx.ATTR_TYPE_FLOAT32, num: 3}
]);
vfmtStretch.name = 'vfmtStretch';
var vfmtMesh = new gfx.VertexFormat([
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD1, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD2, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
{ name: gfx.ATTR_TEX_COORD3, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
{ name: gfx.ATTR_NORMAL, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
{ name: gfx.ATTR_COLOR1, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true }
]);
vfmtMesh.name = 'vfmtMesh';
@ccclass('cc.ParticleSystem3DAssembler')
export default class ParticleSystem3DAssembler extends Assembler {
_defines = null;
_trailDefines = null;
_model = null;
frameTile_velLenScale = null;
attrs = [];
_vertFormat = [];
_particleSystem = null;
_particles = null;
_defaultMat = null;
_isAssetReady = false;
_defaultTrailMat = null;
_customProperties = null;
_node_scale = null;
constructor () {
super();
this._model = null;
this.frameTile_velLenScale = cc.v4(1, 1, 0, 0);
this._node_scale = cc.v4();
this.attrs = new Array(5);
this._trailDefines = {
CC_USE_WORLD_SPACE: true,
//CC_DRAW_WIRE_FRAME: true, // <wireframe debug>
};
}
onInit (ps) {
this._particleSystem = ps;
this._particles = new RecyclePool(() => {
return new Particle(this);
}, 16);
this._setVertexAttrib();
this.onEnable();
this._updateModel();
this._updateMaterialParams();
this._updateTrailMaterial();
}
onEnable () {
if (!this._particleSystem) {
return;
}
if (this._model == null) {
this._model = new ParticleBatchModel();
}
if (!this._model.inited) {
this._model.setCapacity(this._particleSystem.capacity);
}
this._model.enabled = this._particleSystem.enabledInHierarchy;
}
onDisable () {
if (this._model) {
this._model.enabled = this._particleSystem.enabledInHierarchy;
}
}
onDestroy () {
this._model = null;
}
clear () {
this._particles.reset();
this.updateParticleBuffer();
}
_getFreeParticle () {
if (this._particles.length >= this._particleSystem.capacity) {
return null;
}
return this._particles.add();
}
_setNewParticle (p) {
}
_updateParticles (dt) {
this._particleSystem.node.getWorldMatrix(_tempWorldTrans);
switch (this._particleSystem.scaleSpace) {
case Space.Local:
this._particleSystem.node.getScale(this._node_scale);
break;
case Space.World:
this._particleSystem.node.getWorldScale(this._node_scale);
break;
}
let material = this._particleSystem.materials[0];
let mat = material ? this._particleSystem.particleMaterial : this._defaultMat;
mat.setProperty('scale', this._node_scale);
if (this._particleSystem.velocityOvertimeModule.enable) {
this._particleSystem.velocityOvertimeModule.update(this._particleSystem._simulationSpace, _tempWorldTrans);
}
if (this._particleSystem.forceOvertimeModule.enable) {
this._particleSystem.forceOvertimeModule.update(this._particleSystem._simulationSpace, _tempWorldTrans);
}
if (this._particleSystem.trailModule.enable) {
this._particleSystem.trailModule.update();
}
for (let i = 0; i < this._particles.length; ++i) {
const p = this._particles.data[i];
p.remainingLifetime -= dt;
Vec3.set(p.animatedVelocity, 0, 0, 0);
if (p.remainingLifetime < 0.0) {
if (this._particleSystem.trailModule.enable) {
this._particleSystem.trailModule.removeParticle(p);
}
this._particles.remove(i);
--i;
continue;
}
p.velocity.y -= this._particleSystem.gravityModifier.evaluate(1 - p.remainingLifetime / p.startLifetime, p.randomSeed) * 9.8 * dt; // apply gravity.
if (this._particleSystem.sizeOvertimeModule.enable) {
this._particleSystem.sizeOvertimeModule.animate(p);
}
if (this._particleSystem.colorOverLifetimeModule.enable) {
this._particleSystem.colorOverLifetimeModule.animate(p);
}
if (this._particleSystem.forceOvertimeModule.enable) {
this._particleSystem.forceOvertimeModule.animate(p, dt);
}
if (this._particleSystem.velocityOvertimeModule.enable) {
this._particleSystem.velocityOvertimeModule.animate(p);
} else {
Vec3.copy(p.ultimateVelocity, p.velocity);
}
if (this._particleSystem.limitVelocityOvertimeModule.enable) {
this._particleSystem.limitVelocityOvertimeModule.animate(p);
}
if (this._particleSystem.rotationOvertimeModule.enable) {
this._particleSystem.rotationOvertimeModule.animate(p, dt);
}
if (this._particleSystem.textureAnimationModule.enable) {
this._particleSystem.textureAnimationModule.animate(p);
}
Vec3.scaleAndAdd(p.position, p.position, p.ultimateVelocity, dt); // apply velocity.
if (this._particleSystem.trailModule.enable) {
this._particleSystem.trailModule.animate(p, dt);
}
}
return this._particles.length;
}
// internal function
updateParticleBuffer () {
// update vertex buffer
let idx = 0;
const uploadVel = this._particleSystem.renderMode === RenderMode.StrecthedBillboard;
for (let i = 0; i < this._particles.length; ++i) {
const p = this._particles.data[i];
let fi = 0;
if (this._particleSystem.textureAnimationModule.enable) {
fi = p.frameIndex;
}
idx = i * 4;
let attrNum = 0;
if (this._particleSystem.renderMode !== RenderMode.Mesh) {
for (let j = 0; j < 4; ++j) { // four verts per particle.
attrNum = 0;
this.attrs[attrNum++] = p.position;
_tempAttribUV.x = _uvs[2 * j];
_tempAttribUV.y = _uvs[2 * j + 1];
_tempAttribUV.z = fi;
this.attrs[attrNum++] = _tempAttribUV;
this.attrs[attrNum++] = p.size;
this.attrs[attrNum++] = p.rotation;
this.attrs[attrNum++] = p.color._val;
if (uploadVel) {
this.attrs[attrNum++] = p.ultimateVelocity;
} else {
this.attrs[attrNum++] = null;
}
this._model.addParticleVertexData(idx++, this.attrs);
}
} else {
attrNum = 0;
this.attrs[attrNum++] = p.position;
_tempAttribUV.z = fi;
this.attrs[attrNum++] = _tempAttribUV;
this.attrs[attrNum++] = p.size;
this.attrs[attrNum++] = p.rotation;
this.attrs[attrNum++] = p.color._val;
this._model.addParticleVertexData(i, this.attrs);
}
}
this.updateIA(0, this._particles.length * this._model._indexCount, true);
}
updateShaderUniform () {
}
updateIA (index, count, vDirty, iDirty) {
if (!this._model) return;
this._model.updateIA(index, count, vDirty, iDirty);
}
getParticleCount () {
return this._particles.data.length;
}
_onMaterialModified (index, material) {
if (index === 0) {
this._updateModel();
this._updateMaterialParams();
} else {
this._updateTrailMaterial();
}
}
_onRebuildPSO (index, material) {
if (this._model && index === 0) {
this._model.setModelMaterial(material);
}
if (this._particleSystem.trailModule._trailModel && index === 1) {
this._particleSystem.trailModule._trailModel.setModelMaterial(material);
}
}
_ensureLoadMesh () {
if (this._particleSystem.mesh && !this._particleSystem.mesh.loaded) {
cc.assetManager.postLoadNative(this._particleSystem.mesh);
}
}
setCapacity (capacity) {
if (!this._model) return;
this._model.setCapacity(capacity);
}
_setVertexAttrib () {
switch (this._particleSystem.renderMode) {
case RenderMode.StrecthedBillboard:
this._vertFormat = vfmtStretch;
break;
case RenderMode.Mesh:
this._vertFormat = vfmtMesh;
break;
default:
this._vertFormat = vfmtNormal;
}
}
_updateMaterialParams () {
if (!this._particleSystem) {
return;
}
let mat = this._particleSystem.materials[0];
if (mat == null && this._defaultMat == null) {
mat = this._defaultMat = MaterialVariant.createWithBuiltin('3d-particle', this);
} else {
mat = MaterialVariant.create(mat, this._particleSystem);
}
mat = mat || this._defaultMat;
if (this._particleSystem._simulationSpace === Space.World) {
mat.define(CC_USE_WORLD_SPACE, true);
} else {
mat.define(CC_USE_WORLD_SPACE, false);
}
if (this._particleSystem.renderMode === RenderMode.Billboard) {
mat.define(CC_USE_BILLBOARD, true);
mat.define(CC_USE_STRETCHED_BILLBOARD, false);
mat.define(CC_USE_HORIZONTAL_BILLBOARD, false);
mat.define(CC_USE_VERTICAL_BILLBOARD, false);
mat.define(CC_USE_MESH, false);
} else if (this._particleSystem.renderMode === RenderMode.StrecthedBillboard) {
mat.define(CC_USE_BILLBOARD, false);
mat.define(CC_USE_STRETCHED_BILLBOARD, true);
mat.define(CC_USE_HORIZONTAL_BILLBOARD, false);
mat.define(CC_USE_VERTICAL_BILLBOARD, false);
mat.define(CC_USE_MESH, false);
this.frameTile_velLenScale.z = this._particleSystem.velocityScale;
this.frameTile_velLenScale.w = this._particleSystem.lengthScale;
} else if (this._particleSystem.renderMode === RenderMode.HorizontalBillboard) {
mat.define(CC_USE_BILLBOARD, false);
mat.define(CC_USE_STRETCHED_BILLBOARD, false);
mat.define(CC_USE_HORIZONTAL_BILLBOARD, true);
mat.define(CC_USE_VERTICAL_BILLBOARD, false);
mat.define(CC_USE_MESH, false);
} else if (this._particleSystem.renderMode === RenderMode.VerticalBillboard) {
mat.define(CC_USE_BILLBOARD, false);
mat.define(CC_USE_STRETCHED_BILLBOARD, false);
mat.define(CC_USE_HORIZONTAL_BILLBOARD, false);
mat.define(CC_USE_VERTICAL_BILLBOARD, true);
mat.define(CC_USE_MESH, false);
} else if (this._particleSystem.renderMode === RenderMode.Mesh) {
mat.define(CC_USE_BILLBOARD, false);
mat.define(CC_USE_STRETCHED_BILLBOARD, false);
mat.define(CC_USE_HORIZONTAL_BILLBOARD, false);
mat.define(CC_USE_VERTICAL_BILLBOARD, false);
mat.define(CC_USE_MESH, true);
} else {
console.warn(`particle system renderMode ${this._particleSystem.renderMode} not support.`);
}
if (this._particleSystem.textureAnimationModule.enable) {
Vec2.set(this.frameTile_velLenScale, this._particleSystem.textureAnimationModule.numTilesX, this._particleSystem.textureAnimationModule.numTilesY);
}
mat.setProperty('frameTile_velLenScale', this.frameTile_velLenScale);
this._particleSystem.setMaterial(0, mat);
}
_updateTrailMaterial () {
// Here need to create a material variant through the getter call.
let mat = this._particleSystem.trailMaterial;
if (this._particleSystem.trailModule.enable) {
if (mat === null && this._defaultTrailMat === null) {
this._defaultTrailMat = MaterialVariant.createWithBuiltin('3d-trail', this);
}
if (mat === null) {
mat = this._defaultTrailMat;
this._particleSystem.trailMaterial = mat;
}
if (this._particleSystem._simulationSpace === Space.World || this._particleSystem.trailModule.space === Space.World) {
mat.define(CC_USE_WORLD_SPACE, true);
} else {
mat.define(CC_USE_WORLD_SPACE, false);
}
//mat.define(CC_DRAW_WIRE_FRAME, true); // <wireframe debug>
this._particleSystem.trailModule._updateMaterial();
}
}
_updateTrailEnable (enable) {
if (!this._model) {
return;
}
let subData = this._model._subDatas[1];
if (subData) {
subData.enable = enable;
}
}
_updateModel () {
if (!this._model) {
return;
}
this._model.setVertexAttributes(this._particleSystem.renderMode === RenderMode.Mesh ? this._particleSystem.mesh : null, this._vertFormat);
}
setVertexAttributes (mesh, vfmt) {
if (!this._model) {
return;
}
this._model.setVertexAttributes(mesh, vfmt);
}
fillBuffers (comp, renderer) {
if (!this._model) return;
this._model._uploadData();
let submeshes = this._model._subMeshes;
let subDatas = this._model._subDatas;
let materials = comp.materials;
renderer._flush()
for (let i = 0, len = submeshes.length; i < len; i++) {
let ia = submeshes[i];
let meshData = subDatas[i];
let material = materials[i];
if (meshData.enable) {
renderer.material = material;
renderer.cullingMask = comp.node._cullingMask;
renderer.node = comp.node;
renderer._flushIA(ia);
}
}
}
}
Object.assign(ParticleSystem3DAssembler, { uv: _uvs });
Assembler.register(ParticleSystem3D, ParticleSystem3DAssembler);

View File

@@ -0,0 +1,600 @@
import { ccclass, property } from '../../../platform/CCClassDecorator';
import { Vec3, toRadian, Color} from '../../../value-types';
import gfx from '../../../../renderer/gfx';
import Pool from '../../../../renderer/memop/pool';
import CurveRange from '../animator/curve-range';
import GradientRange from '../animator/gradient-range';
import { Space, TextureMode, TrailMode } from '../enum';
import MapUtils from '../utils';
// tslint:disable: max-line-length
const PRE_TRIANGLE_INDEX = 1;
const NEXT_TRIANGLE_INDEX = 1 << 2;
const DIRECTION_THRESHOLD = Math.cos(toRadian(100));
const _temp_trailEle = { position: cc.v3(), velocity: cc.v3() };
const _temp_quat = cc.quat();
const _temp_xform = cc.mat4();
const _temp_Vec3 = cc.v3();
const _temp_Vec3_1 = cc.v3();
const _temp_color = cc.color();
// var barycentric = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // <wireframe debug>
// var _bcIdx = 0;
class ITrailElement {
position;
lifetime;
width;
velocity;
color;
}
// the valid element is in [start,end) range.if start equals -1,it represents the array is empty.
class TrailSegment {
start;
end;
trailElements = [];
constructor (maxTrailElementNum) {
this.start = -1;
this.end = -1;
this.trailElements = [];
while (maxTrailElementNum--) {
this.trailElements.push({
position: cc.v3(),
lifetime: 0,
width: 0,
velocity: cc.v3(),
direction: 0,
color: cc.color(),
});
}
}
getElement (idx) {
if (this.start === -1) {
return null;
}
if (idx < 0) {
idx = (idx + this.trailElements.length) % this.trailElements.length;
}
if (idx >= this.trailElements.length) {
idx %= this.trailElements.length;
}
return this.trailElements[idx];
}
addElement () {
if (this.trailElements.length === 0) {
return null;
}
if (this.start === -1) {
this.start = 0;
this.end = 1;
return this.trailElements[0];
}
if (this.start === this.end) {
this.trailElements.splice(this.end, 0, {
position: cc.v3(),
lifetime: 0,
width: 0,
velocity: cc.v3(),
direction: 0,
color: cc.color(),
});
this.start++;
this.start %= this.trailElements.length;
}
const newEleLoc = this.end++;
this.end %= this.trailElements.length;
return this.trailElements[newEleLoc];
}
iterateElement (target, f, p, dt) {
const end = this.start >= this.end ? this.end + this.trailElements.length : this.end;
for (let i = this.start; i < end; i++) {
if (f(target, this.trailElements[i % this.trailElements.length], p, dt)) {
this.start++;
this.start %= this.trailElements.length;
}
}
if (this.start === end) {
this.start = -1;
this.end = -1;
}
}
count () {
if (this.start < this.end) {
return this.end - this.start;
} else {
return this.trailElements.length + this.end - this.start;
}
}
clear () {
this.start = -1;
this.end = -1;
}
}
/**
* !#en The trail module of 3d particle.
* !#zh 3D 粒子拖尾模块
* @class TrailModule
*/
@ccclass('cc.TrailModule')
export default class TrailModule {
@property
_enable = false;
/**
* !#en The enable of trailModule.
* !#zh 是否启用
* @property {Boolean} enable
*/
@property
get enable () {
return this._enable;
}
set enable (val) {
if (val) {
this._createTrailData();
}
if (val && !this._enable) {
this._enable = val;
this._particleSystem._assembler._updateTrailMaterial();
}
this._enable = val;
this._particleSystem._assembler._updateTrailEnable(this._enable);
}
/**
* !#en Sets how particles generate trajectories.
* !#zh 设定粒子生成轨迹的方式。
* @property {TrailMode} mode
*/
@property({
type: TrailMode,
})
mode = TrailMode.Particles;
/**
* !#en Life cycle of trajectory.
* !#zh 轨迹存在的生命周期。
* @property {CurveRange} lifeTime
*/
@property({
type: CurveRange,
})
lifeTime = new CurveRange();
@property
_minParticleDistance = 0.1;
/**
* !#en Minimum spacing between each track particle
* !#zh 每个轨迹粒子之间的最小间距。
* @property {Number} minParticleDistance
*/
@property
get minParticleDistance () {
return this._minParticleDistance;
}
set minParticleDistance (val) {
this._minParticleDistance = val;
this._minSquaredDistance = val * val;
}
@property
_space = Space.World;
/**
* !#en The coordinate system of trajectories.
* !#zh 轨迹设定时的坐标系。
* @property {Space} space
*/
@property({
type: Space,
})
get space () {
return this._space;
}
set space (val) {
this._space = val;
if (this._particleSystem) {
this._particleSystem._assembler._updateTrailMaterial();
}
}
/**
* !#en Whether the particle itself exists.
* !#zh 粒子本身是否存在。
* @property {Boolean} existWithParticles
*/
@property
existWithParticles = true;
/**
* !#en Set the texture fill method
* !#zh 设定纹理填充方式。
* @property {TextureMode} textureMode
*/
@property({
type: TextureMode,
})
textureMode = TextureMode.Stretch;
/**
* !#en Whether to use particle width
* !#zh 是否使用粒子的宽度。
* @property {Boolean} widthFromParticle
*/
@property
widthFromParticle = true;
/**
* !#en Curves that control track length
* !#zh 控制轨迹长度的曲线。
* @property {CurveRange} widthRatio
*/
@property({
type: CurveRange,
})
widthRatio = new CurveRange();
/**
* !#en Whether to use particle color
* !#zh 是否使用粒子的颜色。
* @property {Boolean} colorFromParticle
*/
@property
colorFromParticle = false;
/**
* !#en The color of trajectories.
* !#zh 轨迹的颜色。
* @property {GradientRange} colorOverTrail
*/
@property({
type: GradientRange,
})
colorOverTrail = new GradientRange();
/**
* !#en Trajectories color over time.
* !#zh 轨迹随时间变化的颜色。
* @property {GradientRange} colorOvertime
*/
@property({
type: GradientRange,
})
colorOvertime = new GradientRange();
_particleSystem = null;
_minSquaredDistance = 0;
_vertSize = 0;
_trailNum = 0;
_trailLifetime = 0;
vbOffset = 0;
ibOffset = 0;
_trailSegments = null;
_particleTrail = null;
_ia = null;
_gfxVFmt = null;
_vbF32 = null;
_vbUint32 = null;
_iBuffer = null;
_needTransform = null;
_defaultMat = null;
_material = null;
constructor () {
this._gfxVFmt = new gfx.VertexFormat([
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_TEX_COORD, type: gfx.ATTR_TYPE_FLOAT32, num: 4},
//{ name: gfx.ATTR_TEX_COORD2, type: gfx.ATTR_TYPE_FLOAT32, num: 3 }, // <wireframe debug>
{ name: gfx.ATTR_TEX_COORD1, type: gfx.ATTR_TYPE_FLOAT32, num: 3},
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
]);
this._vertSize = this._gfxVFmt._bytes;
this._particleTrail = new MapUtils(); // Map<Particle, TrailSegment>();
}
onInit (ps) {
this._particleSystem = ps;
this.minParticleDistance = this._minParticleDistance;
let burstCount = 0;
for (const b of ps.bursts) {
burstCount += b.getMaxCount(ps);
}
this.lifeTime.constant = 1;
this._trailNum = Math.ceil(ps.startLifetime.getMax() * this.lifeTime.getMax() * 60 * (ps.rateOverTime.getMax() * ps.duration + burstCount));
this._trailSegments = new Pool(() => new TrailSegment(10), Math.ceil(ps.rateOverTime.getMax() * ps.duration));
if (this._enable) {
this.enable = this._enable;
this._updateMaterial();
}
}
onEnable () {
}
onDisable () {
}
destroy () {
if (this._trailSegments) {
this._trailSegments.clear((obj) => { obj.trailElements.length = 0; });
this._trailSegments = null;
}
}
clear () {
if (this.enable) {
const trailIter = this._particleTrail.values();
let trail = trailIter.next();
while (!trail.done) {
trail.value.clear();
trail = trailIter.next();
}
this._particleTrail.clear();
this.updateTrailBuffer();
}
}
_createTrailData () {
let model = this._particleSystem._assembler._model;
if (model) {
model.createTrailData(this._gfxVFmt, this._trailNum);
let subData = model._subDatas[1];
this._vbF32 = subData.getVData();
this._vbUint32 = subData.getVData(Uint32Array);
this._iBuffer = subData.iData;
}
}
_updateMaterial () {
if (this._particleSystem) {
const mat = this._particleSystem.trailMaterial;
if (mat) {
this._material = mat;
} else {
this._material = this._particleSystem._assembler._defaultTrailMat;
}
}
}
update () {
this._trailLifetime = this.lifeTime.evaluate(this._particleSystem._time, 1);
if (this.space === Space.World && this._particleSystem._simulationSpace === Space.Local) {
this._needTransform = true;
this._particleSystem.node.getWorldMatrix(_temp_xform);
this._particleSystem.node.getWorldRotation(_temp_quat);
} else {
this._needTransform = false;
}
}
animate (p, scaledDt) {
if (!this._trailSegments) {
return;
}
let trail = this._particleTrail.get(p);
if (!trail) {
trail = this._trailSegments.alloc();
this._particleTrail.set(p, trail);
return;
}
let lastSeg = trail.getElement(trail.end - 1);
if (this._needTransform) {
Vec3.transformMat4(_temp_Vec3, p.position, _temp_xform);
} else {
Vec3.copy(_temp_Vec3, p.position);
}
if (lastSeg) {
trail.iterateElement(this, this._updateTrailElement, p, scaledDt);
if (Vec3.squaredDistance(lastSeg.position, _temp_Vec3) < this._minSquaredDistance) {
return;
}
}
lastSeg = trail.addElement();
if (!lastSeg) {
return;
}
Vec3.copy(lastSeg.position, _temp_Vec3);
lastSeg.lifetime = 0;
if (this.widthFromParticle) {
lastSeg.width = p.size.x * this.widthRatio.evaluate(0, 1);
} else {
lastSeg.width = this.widthRatio.evaluate(0, 1);
}
const trailNum = trail.count();
if (trailNum === 2) {
const lastSecondTrail = trail.getElement(trail.end - 2);
Vec3.subtract(lastSecondTrail.velocity, lastSeg.position, lastSecondTrail.position);
} else if (trailNum > 2) {
const lastSecondTrail = trail.getElement(trail.end - 2);
const lastThirdTrail = trail.getElement(trail.end - 3);
Vec3.subtract(_temp_Vec3, lastThirdTrail.position, lastSecondTrail.position);
Vec3.subtract(_temp_Vec3_1, lastSeg.position, lastSecondTrail.position);
Vec3.subtract(lastSecondTrail.velocity, _temp_Vec3_1, _temp_Vec3);
if (Vec3.equals(cc.Vec3.ZERO, lastSecondTrail.velocity)) {
Vec3.copy(lastSecondTrail.velocity, _temp_Vec3);
}
}
if (this.colorFromParticle) {
lastSeg.color.set(p.color);
} else {
lastSeg.color.set(this.colorOvertime.evaluate(0, 1));
}
}
_updateTrailElement (trail, trailEle, p, dt) {
trailEle.lifetime += dt;
if (trail.colorFromParticle) {
trailEle.color.set(p.color);
trailEle.color.multiply(trail.colorOvertime.evaluate(1.0 - p.remainingLifetime / p.startLifetime, 1));
} else {
trailEle.color.set(trail.colorOvertime.evaluate(1.0 - p.remainingLifetime / p.startLifetime, 1));
}
if (trail.widthFromParticle) {
trailEle.width = p.size.x * trail.widthRatio.evaluate(trailEle.lifetime / trail._trailLifetime, 1);
} else {
trailEle.width = trail.widthRatio.evaluate(trailEle.lifetime / trail._trailLifetime, 1);
}
return trailEle.lifetime > trail._trailLifetime;
}
removeParticle (p) {
const trail = this._particleTrail.get(p);
if (trail && this._trailSegments) {
trail.clear();
this._trailSegments.free(trail);
this._particleTrail.delete(p);
}
}
updateTrailBuffer () {
this.vbOffset = 0;
this.ibOffset = 0;
for (const p of this._particleTrail.keys()) {
const trailSeg = this._particleTrail.get(p);
if (trailSeg.start === -1) {
continue;
}
const indexOffset = this.vbOffset * 4 / this._vertSize;
const end = trailSeg.start >= trailSeg.end ? trailSeg.end + trailSeg.trailElements.length : trailSeg.end;
const trailNum = end - trailSeg.start;
// const lastSegRatio = Vec3.distance(trailSeg.getTailElement()!.position, p.position) / this._minParticleDistance;
const textCoordSeg = 1 / (trailNum /*- 1 + lastSegRatio*/);
const startSegEle = trailSeg.trailElements[trailSeg.start];
this._fillVertexBuffer(startSegEle, this.colorOverTrail.evaluate(1, 1), indexOffset, 1, 0, NEXT_TRIANGLE_INDEX);
for (let i = trailSeg.start + 1; i < end; i++) {
const segEle = trailSeg.trailElements[i % trailSeg.trailElements.length];
const j = i - trailSeg.start;
this._fillVertexBuffer(segEle, this.colorOverTrail.evaluate(1 - j / trailNum, 1), indexOffset, 1 - j * textCoordSeg, j, PRE_TRIANGLE_INDEX | NEXT_TRIANGLE_INDEX);
}
if (this._needTransform) {
Vec3.transformMat4(_temp_trailEle.position, p.position, _temp_xform);
} else {
Vec3.copy(_temp_trailEle.position, p.position);
}
if (trailNum === 1 || trailNum === 2) {
const lastSecondTrail = trailSeg.getElement(trailSeg.end - 1);
Vec3.subtract(lastSecondTrail.velocity, _temp_trailEle.position, lastSecondTrail.position);
this._vbF32[this.vbOffset - this._vertSize / 4 - 4] = lastSecondTrail.velocity.x;
this._vbF32[this.vbOffset - this._vertSize / 4 - 3] = lastSecondTrail.velocity.y;
this._vbF32[this.vbOffset - this._vertSize / 4 - 2] = lastSecondTrail.velocity.z;
this._vbF32[this.vbOffset - 4] = lastSecondTrail.velocity.x;
this._vbF32[this.vbOffset - 3] = lastSecondTrail.velocity.y;
this._vbF32[this.vbOffset - 2] = lastSecondTrail.velocity.z;
Vec3.subtract(_temp_trailEle.velocity, _temp_trailEle.position, lastSecondTrail.position);
this._checkDirectionReverse(_temp_trailEle, lastSecondTrail);
} else if (trailNum > 2) {
const lastSecondTrail = trailSeg.getElement(trailSeg.end - 1);
const lastThirdTrail = trailSeg.getElement(trailSeg.end - 2);
Vec3.subtract(_temp_Vec3, lastThirdTrail.position, lastSecondTrail.position);
Vec3.subtract(_temp_Vec3_1, _temp_trailEle.position, lastSecondTrail.position);
Vec3.normalize(_temp_Vec3, _temp_Vec3);
Vec3.normalize(_temp_Vec3_1, _temp_Vec3_1);
Vec3.subtract(lastSecondTrail.velocity, _temp_Vec3_1, _temp_Vec3);
Vec3.normalize(lastSecondTrail.velocity, lastSecondTrail.velocity);
this._checkDirectionReverse(lastSecondTrail, lastThirdTrail);
this.vbOffset -= this._vertSize / 4 * 2;
this.ibOffset -= 6;
//_bcIdx = (_bcIdx - 6 + 9) % 9; // <wireframe debug>
this._fillVertexBuffer(lastSecondTrail, this.colorOverTrail.evaluate(textCoordSeg, 1), indexOffset, textCoordSeg, trailNum - 1, PRE_TRIANGLE_INDEX | NEXT_TRIANGLE_INDEX);
Vec3.subtract(_temp_trailEle.velocity, _temp_trailEle.position, lastSecondTrail.position);
Vec3.normalize(_temp_trailEle.velocity, _temp_trailEle.velocity);
this._checkDirectionReverse(_temp_trailEle, lastSecondTrail);
}
if (this.widthFromParticle) {
_temp_trailEle.width = p.size.x * this.widthRatio.evaluate(0, 1);
} else {
_temp_trailEle.width = this.widthRatio.evaluate(0, 1);
}
_temp_trailEle.color = p.color;
if (Vec3.equals(_temp_trailEle.velocity, cc.Vec3.ZERO)) {
this.ibOffset -= 3;
} else {
this._fillVertexBuffer(_temp_trailEle, this.colorOverTrail.evaluate(0, 1), indexOffset, 0, trailNum, PRE_TRIANGLE_INDEX);
}
}
this._updateIA(this.ibOffset);
}
_fillVertexBuffer (trailSeg, colorModifer, indexOffset, xTexCoord, trailEleIdx, indexSet) {
this._vbF32[this.vbOffset++] = trailSeg.position.x;
this._vbF32[this.vbOffset++] = trailSeg.position.y;
this._vbF32[this.vbOffset++] = trailSeg.position.z;
this._vbF32[this.vbOffset++] = 0;
this._vbF32[this.vbOffset++] = trailSeg.width;
this._vbF32[this.vbOffset++] = xTexCoord;
this._vbF32[this.vbOffset++] = 0;
// this._vbF32[this.vbOffset++] = barycentric[_bcIdx++]; // <wireframe debug>
// this._vbF32[this.vbOffset++] = barycentric[_bcIdx++];
// this._vbF32[this.vbOffset++] = barycentric[_bcIdx++];
// _bcIdx %= 9;
this._vbF32[this.vbOffset++] = trailSeg.velocity.x;
this._vbF32[this.vbOffset++] = trailSeg.velocity.y;
this._vbF32[this.vbOffset++] = trailSeg.velocity.z;
_temp_color.set(trailSeg.color);
_temp_color.multiply(colorModifer);
this._vbUint32[this.vbOffset++] = _temp_color._val;
this._vbF32[this.vbOffset++] = trailSeg.position.x;
this._vbF32[this.vbOffset++] = trailSeg.position.y;
this._vbF32[this.vbOffset++] = trailSeg.position.z;
this._vbF32[this.vbOffset++] = 1;
this._vbF32[this.vbOffset++] = trailSeg.width;
this._vbF32[this.vbOffset++] = xTexCoord;
this._vbF32[this.vbOffset++] = 1;
// this._vbF32[this.vbOffset++] = barycentric[_bcIdx++]; // <wireframe debug>
// this._vbF32[this.vbOffset++] = barycentric[_bcIdx++];
// this._vbF32[this.vbOffset++] = barycentric[_bcIdx++];
// _bcIdx %= 9;
this._vbF32[this.vbOffset++] = trailSeg.velocity.x;
this._vbF32[this.vbOffset++] = trailSeg.velocity.y;
this._vbF32[this.vbOffset++] = trailSeg.velocity.z;
this._vbUint32[this.vbOffset++] = _temp_color._val;
if (indexSet & PRE_TRIANGLE_INDEX) {
this._iBuffer[this.ibOffset++] = indexOffset + 2 * trailEleIdx;
this._iBuffer[this.ibOffset++] = indexOffset + 2 * trailEleIdx - 1;
this._iBuffer[this.ibOffset++] = indexOffset + 2 * trailEleIdx + 1;
}
if (indexSet & NEXT_TRIANGLE_INDEX) {
this._iBuffer[this.ibOffset++] = indexOffset + 2 * trailEleIdx;
this._iBuffer[this.ibOffset++] = indexOffset + 2 * trailEleIdx + 1;
this._iBuffer[this.ibOffset++] = indexOffset + 2 * trailEleIdx + 2;
}
}
_updateIA (count) {
if (this._particleSystem && this._particleSystem._assembler) {
this._particleSystem._assembler.updateIA(1, count, true, true);
}
}
_checkDirectionReverse (currElement, prevElement) {
if (Vec3.dot(currElement.velocity, prevElement.velocity) < DIRECTION_THRESHOLD) {
currElement.direction = 1 - prevElement.direction;
} else {
currElement.direction = prevElement.direction;
}
}
}

View File

@@ -0,0 +1,120 @@
// SameValue algorithm
if (!Object.is) {
Object.is = function(x, y) {
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
};
}
/**
* !#en
* Helper class for ES5 Map.
* !#zh
* ES5 Map 辅助类。
* @class MapUtils
*/
export default class MapUtils {
datas = [];
constructor (data) {
!data && (data = []);
this.datas = [];
let that = this;
data.forEach(function (item) {
if (!that.has(item[0])) {
that.datas.push({
key: item[0],
value: item[1]
});
}
});
}
size () {
return this.datas.length;
}
set (key, value) {
this.delete(key);
this.datas.push({
key: key,
value: value
});
}
get (key) {
let value = undefined;
let datas = this.datas;
for (let i = 0, len = datas.length; i < len; i++) {
if (Object.is(key, datas[i].key)) {
value = datas[i].value;
break;
}
}
return value;
}
has (key) {
let res = false;
let datas = this.datas;
for (let i = 0, len = datas.length; i < len; i++) {
if (Object.is(key, datas[i].key)) {
res = true;
break;
}
}
return res;
}
clear () {
this.datas.length = 0;
}
delete (key) {
let res = false;
let datas = this.datas;
for (let i = 0, len = datas.length; i < len; i++) {
if (Object.is(key, datas[i].key)) {
datas.splice(i, 1);
res = true;
break;
}
}
return res;
}
keys () {
let datas = this.datas;
let keys = [];
for (let i = 0, len = datas.length; i < len; i++) {
keys.push(datas[i].key);
}
return keys;
}
values () {
let index = 0;
let datas = this.datas;
return {
next: function () {
if (datas.length === 0 || datas[index] === undefined) {
return {
value: undefined,
done: true
};
}
return {
value: datas[index++].value,
done: false
};
}
};
};
};

View File

@@ -0,0 +1,287 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import CANNON from '../../../../../external/cannon/cannon';
import { IRigidBody } from '../spec/I-rigid-body';
import { CannonSharedBody } from './cannon-shared-body';
import { CannonWorld } from './cannon-world';
import { RigidBody3D } from '../framework';
const v3_cannon0 = new CANNON.Vec3();
const v3_cannon1 = new CANNON.Vec3();
const Vec3 = cc.Vec3;
/**
* wraped shared body
* dynamic
* kinematic
*/
export class CannonRigidBody implements IRigidBody {
get isAwake (): boolean {
return this._sharedBody.body.isAwake();
}
get isSleepy (): boolean {
return this._sharedBody.body.isSleepy();
}
get isSleeping (): boolean {
return this._sharedBody.body.isSleeping();
}
set allowSleep (v: boolean) {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.allowSleep = v;
}
set mass (value: number) {
let body = this._sharedBody.body;
body.mass = value;
if (body.mass == 0) {
body.type = CANNON.Body.STATIC;
} else {
body.type = this._rigidBody.isKinematic ? CANNON.Body.KINEMATIC : CANNON.Body.DYNAMIC;
}
body.updateMassProperties();
if (body.isSleeping()) {
body.wakeUp();
}
}
set isKinematic (value: boolean) {
let body = this._sharedBody.body;
if (body.mass == 0) {
body.type = CANNON.Body.STATIC;
} else {
if (value) {
body.type = CANNON.Body.KINEMATIC;
} else {
body.type = CANNON.Body.DYNAMIC;
}
}
}
set fixedRotation (value: boolean) {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.fixedRotation = value;
body.updateMassProperties();
}
set linearDamping (value: number) {
this._sharedBody.body.linearDamping = value;
}
set angularDamping (value: number) {
this._sharedBody.body.angularDamping = value;
}
set useGravity (value: boolean) {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.useGravity = value;
}
set linearFactor (value: cc.Vec3) {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
Vec3.copy(body.linearFactor, value);
}
set angularFactor (value: cc.Vec3) {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
Vec3.copy(body.angularFactor, value);
}
get rigidBody () {
return this._rigidBody;
}
get sharedBody () {
return this._sharedBody;
}
get isEnabled () {
return this._isEnabled;
}
private _rigidBody!: RigidBody3D;
private _sharedBody!: CannonSharedBody;
private _isEnabled = false;
/** LIFECYCLE */
__preload (com: RigidBody3D) {
this._rigidBody = com;
this._sharedBody = (cc.director.getPhysics3DManager().physicsWorld as CannonWorld).getSharedBody(this._rigidBody.node);
this._sharedBody.reference = true;
this._sharedBody.wrappedBody = this;
}
onLoad () {
}
onEnable () {
this._isEnabled = true;
this.mass = this._rigidBody.mass;
this.allowSleep = this._rigidBody.allowSleep;
this.linearDamping = this._rigidBody.linearDamping;
this.angularDamping = this._rigidBody.angularDamping;
this.useGravity = this._rigidBody.useGravity;
this.isKinematic = this._rigidBody.isKinematic;
this.fixedRotation = this._rigidBody.fixedRotation;
this.linearFactor = this._rigidBody.linearFactor;
this.angularFactor = this._rigidBody.angularFactor;
this._sharedBody.enabled = true;
}
onDisable () {
this._isEnabled = false;
this._sharedBody.enabled = false;
}
onDestroy () {
this._sharedBody.reference = false;
(this._rigidBody as any) = null;
(this._sharedBody as any) = null;
}
/** INTERFACE */
wakeUp (): void {
return this._sharedBody.body.wakeUp();
}
sleep (): void {
return this._sharedBody.body.sleep();
}
getLinearVelocity (out: cc.Vec3): cc.Vec3 {
Vec3.copy(out, this._sharedBody.body.velocity);
return out;
}
setLinearVelocity (value: cc.Vec3): void {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
Vec3.copy(body.velocity, value);
}
getAngularVelocity (out: cc.Vec3): cc.Vec3 {
Vec3.copy(out, this._sharedBody.body.angularVelocity);
return out;
}
setAngularVelocity (value: cc.Vec3): void {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
Vec3.copy(body.angularVelocity, value);
}
applyForce (force: cc.Vec3, worldPoint?: cc.Vec3) {
if (worldPoint == null) {
worldPoint = Vec3.ZERO;
}
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.applyForce(Vec3.copy(v3_cannon0, force), Vec3.copy(v3_cannon1, worldPoint));
}
applyImpulse (impulse: cc.Vec3, worldPoint?: cc.Vec3) {
if (worldPoint == null) {
worldPoint = Vec3.ZERO;
}
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.applyImpulse(Vec3.copy(v3_cannon0, impulse), Vec3.copy(v3_cannon1, worldPoint));
}
applyLocalForce (force: cc.Vec3, localPoint?: cc.Vec3): void {
if (localPoint == null) {
localPoint = Vec3.ZERO;
}
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.applyLocalForce(Vec3.copy(v3_cannon0, force), Vec3.copy(v3_cannon1, localPoint));
}
applyLocalImpulse (impulse: cc.Vec3, localPoint?: cc.Vec3): void {
if (localPoint == null) {
localPoint = Vec3.ZERO;
}
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.applyLocalImpulse(Vec3.copy(v3_cannon0, impulse), Vec3.copy(v3_cannon1, localPoint));
}
applyTorque (torque: cc.Vec3): void {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
body.torque.x += torque.x;
body.torque.y += torque.y;
body.torque.z += torque.z;
}
applyLocalTorque (torque: cc.Vec3): void {
let body = this._sharedBody.body;
if (body.isSleeping()) {
body.wakeUp();
}
Vec3.copy(v3_cannon0, torque);
body.vectorToWorldFrame(v3_cannon0, v3_cannon0);
body.torque.x += v3_cannon0.x;
body.torque.y += v3_cannon0.y;
body.torque.z += v3_cannon0.z;
}
}

View File

@@ -0,0 +1,262 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import CANNON from '../../../../../external/cannon/cannon';
import { ERigidBodyType } from '../framework/physics-enum';
import { getWrap, worldDirty } from '../framework/util';
import { CannonWorld } from './cannon-world';
import { CannonShape } from './shapes/cannon-shape';
import { Collider3D } from '../exports/physics-framework';
import { CollisionEventType } from '../framework/physics-interface';
import { CannonRigidBody } from './cannon-rigid-body';
import { commitShapeUpdates, groupIndexToBitMask, deprecatedEventMap } from './cannon-util'
const LocalDirtyFlag = cc.Node._LocalDirtyFlag;
const PHYSICS_SCALE = LocalDirtyFlag.PHYSICS_SCALE;
const Quat = cc.Quat;
const Vec3 = cc.Vec3;
const fastRemoveAt = cc.js.array.fastRemoveAt;
const v3_0 = new Vec3();
const quat_0 = new Quat();
const contactsPool = [] as any;
const CollisionEventObject = {
type: 'collision-enter' as CollisionEventType,
selfCollider: null as Collider3D | null,
otherCollider: null as Collider3D | null,
contacts: [] as any,
};
/**
* sharedbody, node : sharedbody = 1 : 1
* static
*/
export class CannonSharedBody {
private static readonly sharedBodiesMap = new Map<string, CannonSharedBody>();
static getSharedBody (node: cc.Node, wrappedWorld: CannonWorld) {
const key = node._id;
if (CannonSharedBody.sharedBodiesMap.has(key)) {
return CannonSharedBody.sharedBodiesMap.get(key)!;
} else {
const newSB = new CannonSharedBody(node, wrappedWorld);
CannonSharedBody.sharedBodiesMap.set(node._id, newSB);
return newSB;
}
}
readonly node: cc.Node;
readonly wrappedWorld: CannonWorld;
readonly body: CANNON.Body = new CANNON.Body();
readonly shapes: CannonShape[] = [];
wrappedBody: CannonRigidBody | null = null;
private index: number = -1;
private ref: number = 0;
private onCollidedListener = this.onCollided.bind(this);
/**
* add or remove from world \
* add, if enable \
* remove, if disable & shapes.length == 0 & wrappedBody disable
*/
set enabled (v: boolean) {
if (v) {
if (this.index < 0) {
this.index = this.wrappedWorld.bodies.length;
this.wrappedWorld.addSharedBody(this);
var node = this.node;
// body world aabb need to be recalculated
this.body.aabbNeedsUpdate = true;
node.getWorldPosition(v3_0);
node.getWorldRotation(quat_0);
var pos = this.body.position;
pos.x = parseFloat(v3_0.x.toFixed(3));
pos.y = parseFloat(v3_0.y.toFixed(3));
pos.z = parseFloat(v3_0.z.toFixed(3));
var rot = this.body.quaternion;
rot.x = parseFloat(quat_0.x.toFixed(12));
rot.y = parseFloat(quat_0.y.toFixed(12));
rot.z = parseFloat(quat_0.z.toFixed(12));
rot.w = parseFloat(quat_0.w.toFixed(12));
if (node._localMatDirty & PHYSICS_SCALE) {
var wscale = node.__wscale;
for (var i = 0; i < this.shapes.length; i++) {
this.shapes[i].setScale(wscale);
}
commitShapeUpdates(this.body);
}
if (this.body.isSleeping()) {
this.body.wakeUp();
}
}
} else {
if (this.index >= 0) {
const isRemove = (this.shapes.length == 0 && this.wrappedBody == null) ||
(this.shapes.length == 0 && this.wrappedBody != null && !this.wrappedBody.rigidBody.enabledInHierarchy) ||
(this.shapes.length == 0 && this.wrappedBody != null && !this.wrappedBody.isEnabled)
if (isRemove) {
this.body.sleep(); // clear velocity etc.
this.index = -1;
this.wrappedWorld.removeSharedBody(this);
}
}
}
}
set reference (v: boolean) {
v ? this.ref++ : this.ref--;
if (this.ref == 0) { this.destroy(); }
}
private constructor (node: cc.Node, wrappedWorld: CannonWorld) {
this.wrappedWorld = wrappedWorld;
this.node = node;
this.body.material = this.wrappedWorld.world.defaultMaterial;
this.body.addEventListener('cc-collide', this.onCollidedListener);
this._updateGroup();
this.node.on(cc.Node.EventType.GROUP_CHANGED, this._updateGroup, this);
}
_updateGroup () {
groupIndexToBitMask(this.node.groupIndex, this.body);
}
addShape (v: CannonShape) {
const index = this.shapes.indexOf(v);
if (index < 0) {
const index = this.body.shapes.length;
this.body.addShape(v.shape);
this.shapes.push(v);
v.setIndex(index);
const offset = this.body.shapeOffsets[index];
const orient = this.body.shapeOrientations[index];
v.setOffsetAndOrient(offset, orient);
}
}
removeShape (v: CannonShape) {
const index = this.shapes.indexOf(v);
if (index >= 0) {
fastRemoveAt(this.shapes, index);
this.body.removeShape(v.shape);
v.setIndex(-1);
}
}
syncSceneToPhysics (force = false) {
let node = this.node;
let needUpdateTransform = worldDirty(node);
if (!force && !needUpdateTransform) {
return;
}
// body world aabb need to be recalculated
this.body.aabbNeedsUpdate = true;
node.getWorldPosition(v3_0);
node.getWorldRotation(quat_0)
Vec3.copy(this.body.position, v3_0);
Quat.copy(this.body.quaternion, quat_0);
if (node._localMatDirty & PHYSICS_SCALE) {
let wscale = node.__wscale;
for (let i = 0; i < this.shapes.length; i++) {
this.shapes[i].setScale(wscale);
}
commitShapeUpdates(this.body);
}
if (this.body.isSleeping()) {
this.body.wakeUp();
}
}
syncPhysicsToScene () {
if (this.body.type != ERigidBodyType.STATIC && !this.body.isSleeping()) {
Vec3.copy(v3_0, this.body.position);
Quat.copy(quat_0, this.body.quaternion);
this.node.setWorldPosition(v3_0);
this.node.setWorldRotation(quat_0);
}
}
private destroy () {
this.body.removeEventListener('cc-collide', this.onCollidedListener);
this.node.off(cc.Node.EventType.GROUP_CHANGED, this._updateGroup, this);
CannonSharedBody.sharedBodiesMap.delete(this.node._id);
delete CANNON.World['idToBodyMap'][this.body.id];
(this.node as any) = null;
(this.wrappedWorld as any) = null;
(this.body as any) = null;
(this.shapes as any) = null;
(this.onCollidedListener as any) = null;
}
private onCollided (event: CANNON.ICollisionEvent) {
CollisionEventObject.type = event.event;
const self = getWrap<CannonShape>(event.selfShape);
const other = getWrap<CannonShape>(event.otherShape);
if (self) {
CollisionEventObject.selfCollider = self.collider;
CollisionEventObject.otherCollider = other ? other.collider : null;
let i = 0;
for (i = CollisionEventObject.contacts.length; i--;) {
contactsPool.push(CollisionEventObject.contacts.pop());
}
for (i = 0; i < event.contacts.length; i++) {
const cq = event.contacts[i];
if (contactsPool.length > 0) {
const c = contactsPool.pop();
Vec3.copy(c.contactA, cq.ri);
Vec3.copy(c.contactB, cq.rj);
Vec3.copy(c.normal, cq.ni);
CollisionEventObject.contacts.push(c);
} else {
const c = {
contactA: Vec3.copy(new Vec3(), cq.ri),
contactB: Vec3.copy(new Vec3(), cq.rj),
normal: Vec3.copy(new Vec3(), cq.ni),
};
CollisionEventObject.contacts.push(c);
}
}
for (i = 0; i < this.shapes.length; i++) {
const shape = this.shapes[i];
CollisionEventObject.type = deprecatedEventMap[CollisionEventObject.type];
shape.collider.emit(CollisionEventObject.type, CollisionEventObject);
// adapt
CollisionEventObject.type = event.event;
shape.collider.emit(CollisionEventObject.type, CollisionEventObject);
}
}
}
}

View File

@@ -0,0 +1,77 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import CANNON from '../../../../../external/cannon/cannon';
import { getWrap } from '../framework/util';
import { IBaseShape } from '../spec/i-physics-shape';
import { PhysicsRayResult } from '../framework';
import { IRaycastOptions } from '../spec/i-physics-world';
const Vec3 = cc.Vec3;
export function groupIndexToBitMask (groupIndex: number, out: { collisionFilterGroup: number; collisionFilterMask: number; }) {
let categoryBits = 1 << groupIndex;
let maskBits = 0;
let bits = cc.game.collisionMatrix[groupIndex];
if (!bits) {
cc.error("cannon-utils: group is not exist", groupIndex);
return;
}
for (let i = 0; i < bits.length; i++) {
if (!bits[i]) continue;
maskBits |= 1 << i;
}
out.collisionFilterGroup = categoryBits;
out.collisionFilterMask = maskBits;
}
export function toCannonRaycastOptions (out: CANNON.IRaycastOptions, options: IRaycastOptions) {
out.checkCollisionResponse = !options.queryTrigger;
groupIndexToBitMask(options.groupIndex, out);
out.skipBackFaces = false;
}
export function fillRaycastResult (result: PhysicsRayResult, cannonResult: CANNON.RaycastResult) {
result._assign(
Vec3.copy(new Vec3(), cannonResult.hitPointWorld),
cannonResult.distance,
getWrap<IBaseShape>(cannonResult.shape).collider
);
}
export function commitShapeUpdates (body: CANNON.Body) {
body.aabbNeedsUpdate = true;
body.updateMassProperties();
body.updateBoundingRadius();
}
export const deprecatedEventMap = {
'onCollisionEnter': 'collision-enter',
'onCollisionStay': 'collision-stay',
'onCollisionExit': 'collision-exit',
'onTriggerEnter': 'trigger-enter',
'onTriggerStay': 'trigger-stay',
'onTriggerExit': 'trigger-exit',
};

View File

@@ -0,0 +1,183 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import CANNON from '../../../../../external/cannon/cannon';
import { fillRaycastResult, toCannonRaycastOptions } from './cannon-util';
import { CannonShape } from './shapes/cannon-shape';
import { CannonSharedBody } from './cannon-shared-body';
import { IPhysicsWorld, IRaycastOptions } from '../spec/i-physics-world';
import { PhysicsMaterial, PhysicsRayResult } from '../framework';
import { clearNodeTransformRecord, clearNodeTransformDirtyFlag } from '../framework/util'
const Vec3 = cc.Vec3;
const fastRemoveAt = cc.js.array.fastRemoveAt;
export class CannonWorld implements IPhysicsWorld {
get world () {
return this._world;
}
set defaultMaterial (mat: PhysicsMaterial) {
this._world.defaultMaterial.friction = mat.friction;
this._world.defaultMaterial.restitution = mat.restitution;
if (CannonShape.idToMaterial[mat._uuid] != null) {
CannonShape.idToMaterial[mat._uuid] = this._world.defaultMaterial;
}
}
set allowSleep (v: boolean) {
this._world.allowSleep = v;
}
set gravity (gravity: cc.Vec3) {
Vec3.copy(this._world.gravity, gravity);
}
readonly bodies: CannonSharedBody[] = [];
private _world: CANNON.World;
private _raycastResult = new CANNON.RaycastResult();
constructor () {
this._world = new CANNON.World();
this._world.broadphase = new CANNON.NaiveBroadphase();
this._world.addEventListener("postStep", this.onPostStep.bind(this));
}
onPostStep () {
const p3dm = cc.director.getPhysics3DManager();
if (p3dm.useFixedDigit) {
const pd = p3dm.fixDigits.position;
const rd = p3dm.fixDigits.rotation;
const bodies = this._world.bodies;
for (let i = 0; i < bodies.length; i++) {
const bi = bodies[i];
if(bi.type != CANNON.Body.STATIC && !bi.isSleeping()){
const pos = bi.position;
pos.x = parseFloat(pos.x.toFixed(pd));
pos.y = parseFloat(pos.y.toFixed(pd));
pos.z = parseFloat(pos.z.toFixed(pd));
const rot = bi.quaternion;
rot.x = parseFloat(rot.x.toFixed(rd));
rot.y = parseFloat(rot.y.toFixed(rd));
rot.z = parseFloat(rot.z.toFixed(rd));
rot.w = parseFloat(rot.w.toFixed(rd));
const vel = bi.velocity;
vel.x = parseFloat(vel.x.toFixed(pd));
vel.y = parseFloat(vel.y.toFixed(pd));
vel.z = parseFloat(vel.z.toFixed(pd));
const avel = bi.angularVelocity;
avel.x = parseFloat(avel.x.toFixed(pd));
avel.y = parseFloat(avel.y.toFixed(pd));
avel.z = parseFloat(avel.z.toFixed(pd));
}
}
}
}
step (deltaTime: number, timeSinceLastCalled?: number, maxSubStep?: number) {
this.syncSceneToPhysics();
this._world.step(deltaTime, timeSinceLastCalled, maxSubStep);
this.syncPhysicsToScene();
this.emitEvents();
}
syncSceneToPhysics () {
clearNodeTransformRecord();
// sync scene to physics
for (let i = 0; i < this.bodies.length; i++) {
this.bodies[i].syncSceneToPhysics();
}
clearNodeTransformDirtyFlag();
}
syncPhysicsToScene () {
// sync physics to scene
for (let i = 0; i < this.bodies.length; i++) {
this.bodies[i].syncPhysicsToScene();
}
}
emitEvents () {
this._world.emitTriggeredEvents();
this._world.emitCollisionEvents();
}
raycastClosest (worldRay: cc.geomUtils.Ray, options: IRaycastOptions, result: PhysicsRayResult): boolean {
setupFromAndTo(worldRay, options.maxDistance);
toCannonRaycastOptions(raycastOpt, options);
const hit = this._world.raycastClosest(from, to, raycastOpt, this._raycastResult);
if (hit) {
fillRaycastResult(result, this._raycastResult);
}
return hit;
}
raycast (worldRay: cc.geomUtils.Ray, options: IRaycastOptions, pool: cc.RecyclePool, results: PhysicsRayResult[]): boolean {
setupFromAndTo(worldRay, options.maxDistance);
toCannonRaycastOptions(raycastOpt, options);
const hit = this._world.raycastAll(from, to, raycastOpt, (result: CANNON.RaycastResult): any => {
const r = pool.add();
fillRaycastResult(r, result);
results.push(r);
});
return hit
}
getSharedBody (node: Node): CannonSharedBody {
return CannonSharedBody.getSharedBody(node, this);
}
addSharedBody (sharedBody: CannonSharedBody) {
const i = this.bodies.indexOf(sharedBody);
if (i < 0) {
this.bodies.push(sharedBody);
this._world.addBody(sharedBody.body);
}
}
removeSharedBody (sharedBody: CannonSharedBody) {
const i = this.bodies.indexOf(sharedBody);
if (i >= 0) {
fastRemoveAt(this.bodies, i);
this._world.remove(sharedBody.body);
}
}
}
const from = new CANNON.Vec3();
const to = new CANNON.Vec3();
function setupFromAndTo (worldRay: cc.geomUtils.Ray, distance: number) {
Vec3.copy(from, worldRay.o);
worldRay.computeHit(to, distance);
}
const raycastOpt: CANNON.IRaycastOptions = {
'checkCollisionResponse': false,
'collisionFilterGroup': -1,
'collisionFilterMask': -1,
'skipBackFaces': false
}

View File

@@ -0,0 +1,39 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { instantiate } from '../framework/physics-selector';
import { CannonRigidBody } from './cannon-rigid-body';
import { CannonWorld } from './cannon-world';
import { CannonBoxShape } from './shapes/cannon-box-shape';
import { CannonSphereShape } from './shapes/cannon-sphere-shape';
if (CC_PHYSICS_CANNON) {
instantiate(
CannonBoxShape,
CannonSphereShape,
CannonRigidBody,
CannonWorld,
);
}

View File

@@ -0,0 +1,75 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import CANNON from '../../../../../../external/cannon/cannon';
import { commitShapeUpdates } from '../cannon-util';
import { CannonShape } from './cannon-shape';
import { IBoxShape } from '../../spec/i-physics-shape';
import { IVec3Like } from '../../spec/i-common';
import { BoxCollider3D } from '../../exports/physics-framework';
const Vec3 = cc.Vec3;
const v3_0 = new Vec3();
export class CannonBoxShape extends CannonShape implements IBoxShape {
public get boxCollider () {
return this.collider as BoxCollider3D;
}
public get box () {
return this._shape as CANNON.Box;
}
readonly halfExtent: CANNON.Vec3 = new CANNON.Vec3();
constructor (size: cc.Vec3) {
super();
Vec3.multiplyScalar(this.halfExtent, size, 0.5);
this._shape = new CANNON.Box(this.halfExtent.clone());
}
set size (v: IVec3Like) {
this.collider.node.getWorldScale(v3_0);
v3_0.x = Math.abs(v3_0.x);
v3_0.y = Math.abs(v3_0.y);
v3_0.z = Math.abs(v3_0.z);
Vec3.multiplyScalar(this.halfExtent, v, 0.5);
Vec3.multiply(this.box.halfExtents, this.halfExtent, v3_0);
this.box.updateConvexPolyhedronRepresentation();
if (this._index != -1) {
commitShapeUpdates(this._body);
}
}
onLoad () {
super.onLoad();
this.size = this.boxCollider.size;
}
setScale (scale: cc.Vec3): void {
super.setScale(scale);
this.size = this.boxCollider.size;
}
}

View File

@@ -0,0 +1,179 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import CANNON from '../../../../../../external/cannon/cannon';
import { getWrap, setWrap } from '../../framework/util';
import { commitShapeUpdates, deprecatedEventMap } from '../cannon-util';
import { PhysicsMaterial } from '../../framework/assets/physics-material';
import { IBaseShape } from '../../spec/i-physics-shape';
import { IVec3Like } from '../../spec/i-common';
import { CannonSharedBody } from '../cannon-shared-body';
import { CannonWorld } from '../cannon-world';
import { TriggerEventType } from '../../framework/physics-interface';
import { Collider3D } from '../../framework';
const TriggerEventObject = {
type: 'trigger-enter' as TriggerEventType,
selfCollider: null as Collider3D | null,
otherCollider: null as Collider3D | null,
};
const Vec3 = cc.Vec3;
const v3_0 = new Vec3();
export class CannonShape implements IBaseShape {
static readonly idToMaterial = {};
get shape () { return this._shape!; }
get collider () { return this._collider; }
get attachedRigidBody () {
if (this._sharedBody.wrappedBody) { return this._sharedBody.wrappedBody.rigidBody; }
return null;
}
get sharedBody (): CannonSharedBody { return this._sharedBody; }
set material (mat: PhysicsMaterial) {
if (mat == null) {
(this._shape!.material as unknown) = null;
} else {
if (CannonShape.idToMaterial[mat._uuid] == null) {
CannonShape.idToMaterial[mat._uuid] = new CANNON.Material(mat._uuid);
}
this._shape!.material = CannonShape.idToMaterial[mat._uuid];
this._shape!.material.friction = mat.friction;
this._shape!.material.restitution = mat.restitution;
}
}
set isTrigger (v: boolean) {
this._shape.collisionResponse = !v;
if (this._index >= 0) {
this._body.updateHasTrigger();
}
}
set center (v: IVec3Like) {
this._setCenter(v);
if (this._index >= 0) {
commitShapeUpdates(this._body);
}
}
_collider!: Collider3D;
protected _shape!: CANNON.Shape;
protected _offset = new CANNON.Vec3();
protected _orient = new CANNON.Quaternion();
protected _index: number = -1;
protected _sharedBody!: CannonSharedBody;
protected get _body (): CANNON.Body { return this._sharedBody.body; }
protected onTriggerListener = this.onTrigger.bind(this);
/** LIFECYCLE */
__preload (comp: Collider3D) {
this._collider = comp;
setWrap(this._shape, this);
this._shape.addEventListener('cc-trigger', this.onTriggerListener);
this._sharedBody = (cc.director.getPhysics3DManager().physicsWorld as CannonWorld).getSharedBody(this._collider.node);
this._sharedBody.reference = true;
}
onLoad () {
this.center = this._collider.center;
this.isTrigger = this._collider.isTrigger;
}
onEnable () {
this._sharedBody.addShape(this);
this._sharedBody.enabled = true;
}
onDisable () {
this._sharedBody.removeShape(this);
this._sharedBody.enabled = false;
}
onDestroy () {
this._sharedBody.reference = false;
this._shape.removeEventListener('cc-trigger', this.onTriggerListener);
delete CANNON.World['idToShapeMap'][this._shape.id];
(this._sharedBody as any) = null;
setWrap(this._shape, null);
(this._offset as any) = null;
(this._orient as any) = null;
(this._shape as any) = null;
(this._collider as any) = null;
(this.onTriggerListener as any) = null;
}
/**
* change scale will recalculate center & size \
* size handle by child class
* @param scale
*/
setScale (scale: IVec3Like) {
this._setCenter(this._collider.center);
}
setIndex (index: number) {
this._index = index;
}
setOffsetAndOrient (offset: CANNON.Vec3, orient: CANNON.Quaternion) {
cc.Vec3.copy(offset, this._offset);
cc.Vec3.copy(orient, this._orient);
this._offset = offset;
this._orient = orient;
}
protected _setCenter (v: IVec3Like) {
const lpos = this._offset as IVec3Like;
Vec3.copy(lpos, v);
this._collider.node.getWorldScale(v3_0);
Vec3.multiply(lpos, lpos, v3_0);
}
private onTrigger (event: CANNON.ITriggeredEvent) {
TriggerEventObject.type = event.event;
const self = getWrap<CannonShape>(event.selfShape);
const other = getWrap<CannonShape>(event.otherShape);
if (self) {
TriggerEventObject.selfCollider = self.collider;
TriggerEventObject.otherCollider = other ? other.collider : null;
TriggerEventObject.type = deprecatedEventMap[TriggerEventObject.type];
this._collider.emit(TriggerEventObject.type, TriggerEventObject);
// adapt
TriggerEventObject.type = event.event;
this._collider.emit(TriggerEventObject.type, TriggerEventObject);
}
}
}

View File

@@ -0,0 +1,75 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import CANNON from '../../../../../../external/cannon/cannon';
import { commitShapeUpdates } from '../cannon-util';
import { CannonShape } from './cannon-shape';
import { ISphereShape } from '../../spec/i-physics-shape';
import { SphereCollider3D } from '../../exports/physics-framework';
const v3_0 = new cc.Vec3();
export class CannonSphereShape extends CannonShape implements ISphereShape {
get sphereCollider () {
return this.collider as SphereCollider3D;
}
get sphere () {
return this._shape as CANNON.Sphere;
}
get radius () {
return this._radius;
}
set radius (v: number) {
this.collider.node.getWorldScale(v3_0);
const max = v3_0.maxAxis();
this.sphere.radius = v * Math.abs(max);
this.sphere.updateBoundingSphereRadius();
if (this._index != -1) {
commitShapeUpdates(this._body);
}
}
private _radius: number;
constructor (radius: number) {
super();
this._radius = radius;
this._shape = new CANNON.Sphere(this._radius);
}
onLoad () {
super.onLoad();
this.radius = this.sphereCollider.radius;
}
setScale (scale: cc.Vec3): void {
super.setScale(scale);
this.radius = this.sphereCollider.radius;
}
}

View File

@@ -0,0 +1,34 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import {IVec3Like, IQuatLike} from "../spec/i-common"
/**
* declare interface
*/
export interface IBuiltinShape {
center: cc.Vec3;
transform (m: cc.Mat4, pos: IVec3Like, rot: IQuatLike, scale: IVec3Like, out: IBuiltinShape): any;
}

View File

@@ -0,0 +1,147 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { BuiltInWorld } from './builtin-world';
import { BuiltinShape } from './shapes/builtin-shape';
import { worldDirty } from "../framework/util"
const intersect = cc.geomUtils.intersect;
const fastRemove = cc.js.array.fastRemove;
const v3_0 = new cc.Vec3();
const v3_1 = new cc.Vec3();
const quat_0 = new cc.Quat();
/**
* Built-in static collider, no physical forces involved
*/
export class BuiltinSharedBody {
private static readonly sharedBodiesMap = new Map<string, BuiltinSharedBody>();
static getSharedBody (node: cc.Node, wrappedWorld: BuiltInWorld) {
const key = node._id;
if (BuiltinSharedBody.sharedBodiesMap.has(key)) {
return BuiltinSharedBody.sharedBodiesMap.get(key)!;
} else {
const newSB = new BuiltinSharedBody(node, wrappedWorld);
BuiltinSharedBody.sharedBodiesMap.set(node._id, newSB);
return newSB;
}
}
get id () {
return this._id;
}
/**
* add or remove from world \
* add, if enable \
* remove, if disable & shapes.length == 0 & wrappedBody disable
*/
set enabled (v: boolean) {
if (v) {
if (this.index < 0) {
this.index = this.world.bodies.length;
this.world.addSharedBody(this);
this.syncSceneToPhysics(true);
}
} else {
if (this.index >= 0) {
const isRemove = (this.shapes.length == 0);
if (isRemove) {
this.index = -1;
this.world.removeSharedBody(this);
}
}
}
}
set reference (v: boolean) {
v ? this.ref++ : this.ref--;
if (this.ref == 0) { this.destory(); }
}
/** id generator */
private static idCounter: number = 0;
private readonly _id: number;
private index: number = -1;
private ref: number = 0;
readonly node: cc.Node;
readonly world: BuiltInWorld;
readonly shapes: BuiltinShape[] = [];
private constructor (node: cc.Node, world: BuiltInWorld) {
this._id = BuiltinSharedBody.idCounter++;
this.node = node;
this.world = world;
}
intersects (body: BuiltinSharedBody) {
for (let i = 0; i < this.shapes.length; i++) {
const shapeA = this.shapes[i];
for (let j = 0; j < body.shapes.length; j++) {
const shapeB = body.shapes[j];
if (intersect.resolve(shapeA.worldShape, shapeB.worldShape)) {
this.world.shapeArr.push(shapeA);
this.world.shapeArr.push(shapeB);
}
}
}
}
addShape (shape: BuiltinShape): void {
const i = this.shapes.indexOf(shape);
if (i < 0) {
this.shapes.push(shape);
}
}
removeShape (shape: BuiltinShape): void {
fastRemove(this.shapes, shape);
}
syncSceneToPhysics (force: boolean = false) {
let node = this.node;
let needUpdateTransform = worldDirty(node);
if (!force && !needUpdateTransform) return;
node.getWorldPosition(v3_0);
node.getWorldRotation(quat_0)
node.getWorldScale(v3_1);
for (let i = 0; i < this.shapes.length; i++) {
this.shapes[i].transform(node._worldMatrix, v3_0, quat_0, v3_1);
}
}
private destory () {
BuiltinSharedBody.sharedBodiesMap.delete(this.node._id);
(this.node as any) = null;
(this.world as any) = null;
(this.shapes as any) = null;
}
}

View File

@@ -0,0 +1,237 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { PhysicsRayResult } from '../framework/physics-ray-result';
import { BuiltinSharedBody } from './builtin-shared-body';
import { BuiltinShape } from './shapes/builtin-shape';
import { ArrayCollisionMatrix } from './utils/array-collision-matrix';
import { IPhysicsWorld, IRaycastOptions } from '../spec/i-physics-world';
import { IVec3Like } from '../spec/i-common';
import { PhysicsMaterial } from './../framework/assets/physics-material';
import { TriggerEventType } from '../framework/physics-interface';
import { Collider3D } from '../exports/physics-framework';
import { clearNodeTransformRecord, clearNodeTransformDirtyFlag } from '../framework/util';
const fastRemove = cc.js.array.fastRemove;
const intersect = cc.geomUtils.intersect;
const Vec3 = cc.Vec3;
const hitPoint = new Vec3();
const TriggerEventObject = {
type: 'collision-enter' as unknown as TriggerEventType,
selfCollider: null as unknown as Collider3D,
otherCollider: null as unknown as Collider3D,
};
/**
* Built-in collision system, intended for use as a
* efficient discrete collision detector,
* not a full physical simulator
*/
export class BuiltInWorld implements IPhysicsWorld {
set gravity (v: IVec3Like) { }
set allowSleep (v: boolean) { }
set defaultMaterial (v: PhysicsMaterial) { }
readonly shapeArr: BuiltinShape[] = [];
readonly bodies: BuiltinSharedBody[] = [];
private _shapeArrOld: BuiltinShape[] = [];
private _collisionMatrix: ArrayCollisionMatrix = new ArrayCollisionMatrix();
private _collisionMatrixPrev: ArrayCollisionMatrix = new ArrayCollisionMatrix();
step (): void {
// store and reset collsion array
this._shapeArrOld = this.shapeArr.slice();
this.shapeArr.length = 0;
clearNodeTransformRecord();
// sync scene to collision
for (let i = 0; i < this.bodies.length; i++) {
this.bodies[i].syncSceneToPhysics();
}
clearNodeTransformDirtyFlag();
const collisionMatrix = cc.game.collisionMatrix;
// collision detection
for (let i = 0; i < this.bodies.length; i++) {
const bodyA = this.bodies[i];
const nodeA = bodyA.node;
const nodeACollisionMatrix = collisionMatrix[nodeA.groupIndex];
if (!nodeACollisionMatrix) continue;
for (let j = i + 1; j < this.bodies.length; j++) {
const bodyB = this.bodies[j];
const nodeB = bodyB.node;
if (nodeA !== nodeB && nodeACollisionMatrix[nodeB.groupIndex]) {
bodyA.intersects(bodyB);
}
}
}
// emit collider event
this.emitColliderEvent();
}
raycastClosest (worldRay: cc.geomUtils.Ray, options: IRaycastOptions, out: PhysicsRayResult): boolean {
let tmp_d = Infinity;
const max_d = options.maxDistance!;
const groupIndex = options.groupIndex!;
const collisionMatrix = cc.game.collisionMatrix;
const rayCollisionMatrix = collisionMatrix[groupIndex];
if (!rayCollisionMatrix) return false;
for (let i = 0; i < this.bodies.length; i++) {
const body = this.bodies[i] as BuiltinSharedBody;
const bodyGroupIndex = body.node.groupIndex;
const canCollider = rayCollisionMatrix[bodyGroupIndex];
if (!canCollider) continue;
for (let i = 0; i < body.shapes.length; i++) {
const shape = body.shapes[i];
const distance = intersect.resolve(worldRay, shape.worldShape);
if (distance == 0 || distance > max_d) {
continue;
}
if (tmp_d > distance) {
tmp_d = distance;
Vec3.normalize(hitPoint, worldRay.d)
Vec3.scaleAndAdd(hitPoint, worldRay.o, hitPoint, distance);
out._assign(hitPoint, distance, shape.collider);
}
}
}
return !(tmp_d == Infinity);
}
raycast (worldRay: cc.geomUtils.Ray, options: IRaycastOptions, pool: cc.RecyclePool, results: PhysicsRayResult[]): boolean {
const max_d = options.maxDistance!;
const groupIndex = options.groupIndex!;
const collisionMatrix = cc.game.collisionMatrix;
const rayCollisionMatrix = collisionMatrix[groupIndex];
if (!rayCollisionMatrix) return false;
for (let i = 0; i < this.bodies.length; i++) {
const body = this.bodies[i] as BuiltinSharedBody;
const bodyGroupIndex = body.node.groupIndex;
const canCollider = rayCollisionMatrix[bodyGroupIndex];
if (!canCollider) continue;
for (let i = 0; i < body.shapes.length; i++) {
const shape = body.shapes[i];
const distance = intersect.resolve(worldRay, shape.worldShape);
if (distance == 0 || distance > max_d) {
continue;
} else {
const r = pool.add();
worldRay.computeHit(hitPoint, distance);
r._assign(hitPoint, distance, shape.collider);
results.push(r);
}
}
}
return results.length > 0;
}
getSharedBody (node: cc.Node): BuiltinSharedBody {
return BuiltinSharedBody.getSharedBody(node, this);
}
addSharedBody (body: BuiltinSharedBody) {
const index = this.bodies.indexOf(body);
if (index < 0) {
this.bodies.push(body);
}
}
removeSharedBody (body: BuiltinSharedBody) {
fastRemove(this.bodies, body);
}
private emitColliderEvent () {
let shapeA: BuiltinShape;
let shapeB: BuiltinShape;
for (let i = 0; i < this.shapeArr.length; i += 2) {
shapeA = this.shapeArr[i];
shapeB = this.shapeArr[i + 1];
TriggerEventObject.selfCollider = shapeA.collider;
TriggerEventObject.otherCollider = shapeB.collider;
this._collisionMatrix.set(shapeA.id, shapeB.id, true);
if (this._collisionMatrixPrev.get(shapeA.id, shapeB.id)) {
// emit stay
TriggerEventObject.type = 'trigger-stay';
} else {
// first collider, emit enter
TriggerEventObject.type = 'trigger-enter';
}
if (shapeA.collider) {
shapeA.collider.emit(TriggerEventObject.type, TriggerEventObject);
}
TriggerEventObject.selfCollider = shapeB.collider;
TriggerEventObject.otherCollider = shapeA.collider;
if (shapeB.collider) {
shapeB.collider.emit(TriggerEventObject.type, TriggerEventObject);
}
}
for (let i = 0; i < this._shapeArrOld.length; i += 2) {
shapeA = this._shapeArrOld[i];
shapeB = this._shapeArrOld[i + 1];
if (this._collisionMatrixPrev.get(shapeA.id, shapeB.id)) {
if (!this._collisionMatrix.get(shapeA.id, shapeB.id)) {
// emit exit
TriggerEventObject.type = 'trigger-exit';
TriggerEventObject.selfCollider = shapeA.collider;
TriggerEventObject.otherCollider = shapeB.collider;
if (shapeA.collider) {
shapeA.collider.emit(TriggerEventObject.type, TriggerEventObject);
}
TriggerEventObject.selfCollider = shapeB.collider;
TriggerEventObject.otherCollider = shapeA.collider;
if (shapeB.collider) {
shapeB.collider.emit(TriggerEventObject.type, TriggerEventObject);
}
this._collisionMatrix.set(shapeA.id, shapeB.id, false);
}
}
}
this._collisionMatrixPrev.matrix = this._collisionMatrix.matrix.slice();
this._collisionMatrix.reset();
}
}

View File

@@ -0,0 +1,38 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { instantiate } from '../framework/physics-selector';
import { BuiltInWorld } from './builtin-world';
import { BuiltinBoxShape } from './shapes/builtin-box-shape';
import { BuiltinSphereShape } from './shapes/builtin-sphere-shape';
if (CC_PHYSICS_BUILTIN) {
instantiate(
BuiltinBoxShape,
BuiltinSphereShape,
null,
BuiltInWorld,
);
}

View File

@@ -0,0 +1,70 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { BuiltinShape } from './builtin-shape';
import { IBoxShape } from '../../spec/i-physics-shape';
import { BoxCollider3D } from '../../exports/physics-framework';
const Obb = cc.geomUtils.Obb;
const Vec3 = cc.Vec3;
let _worldScale = new Vec3();
export class BuiltinBoxShape extends BuiltinShape implements IBoxShape {
get localObb () {
return this._localShape as cc.geomUtils.Obb;
}
get worldObb () {
return this._worldShape as cc.geomUtils.Obb;
}
public get boxCollider () {
return this.collider as BoxCollider3D;
}
constructor (size: cc.Vec3) {
super();
this._localShape = new Obb();
this._worldShape = new Obb();
Vec3.multiplyScalar(this.localObb.halfExtents, size, 0.5);
Vec3.copy(this.worldObb.halfExtents, this.localObb.halfExtents);
}
set size (size: cc.Vec3) {
Vec3.multiplyScalar(this.localObb.halfExtents, size, 0.5);
this.collider.node.getWorldScale(_worldScale);
_worldScale.x = Math.abs(_worldScale.x);
_worldScale.y = Math.abs(_worldScale.y);
_worldScale.z = Math.abs(_worldScale.z);
Vec3.multiply(this.worldObb.halfExtents, this.localObb.halfExtents, _worldScale);
}
onLoad () {
super.onLoad();
this.size = this.boxCollider.size;
}
}

View File

@@ -0,0 +1,100 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { BuiltinSharedBody } from '../builtin-shared-body';
import { IBuiltinShape } from '../builtin-interface';
import { Collider3D, PhysicsMaterial, RigidBody3D } from '../../exports/physics-framework';
import { IBaseShape } from '../../spec/i-physics-shape';
import { IVec3Like } from '../../spec/i-common';
import { BuiltInWorld } from '../builtin-world';
const Vec3 = cc.Vec3;
export class BuiltinShape implements IBaseShape {
set material (v: PhysicsMaterial) { }
set isTrigger (v: boolean) { }
get attachedRigidBody (): RigidBody3D | null { return null; }
set center (v: IVec3Like) {
Vec3.copy(this._localShape.center, v);
}
get localShape () {
return this._worldShape;
}
get worldShape () {
return this._worldShape;
}
get sharedBody () {
return this._sharedBody;
}
get collider () {
return this._collider;
}
/** id generator */
private static idCounter: number = 0;
readonly id: number = BuiltinShape.idCounter++;;
protected _sharedBody!: BuiltinSharedBody;
protected _collider!: Collider3D;
protected _localShape!: IBuiltinShape;
protected _worldShape!: IBuiltinShape;
__preload (comp: Collider3D) {
this._collider = comp;
this._sharedBody = (cc.director.getPhysics3DManager().physicsWorld as BuiltInWorld).getSharedBody(this._collider.node);
this._sharedBody.reference = true;
}
onLoad () {
this.center = this._collider.center;
}
onEnable () {
this._sharedBody.addShape(this);
this._sharedBody.enabled = true;
}
onDisable () {
this._sharedBody.removeShape(this);
this._sharedBody.enabled = false;
}
onDestroy () {
this._sharedBody.reference = false;
(this._collider as any) = null;
(this._localShape as any) = null;
(this._worldShape as any) = null;
}
transform (m: cc.Mat4, pos: cc.Vec3, rot: cc.Quat, scale: cc.Vec3) {
this._localShape.transform(m, pos, rot, scale, this._worldShape);
}
}

View File

@@ -0,0 +1,65 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { BuiltinShape } from './builtin-shape';
import { ISphereShape } from '../../spec/i-physics-shape';
import { SphereCollider3D } from '../../exports/physics-framework';
const Sphere = cc.geomUtils.Sphere;
let _worldScale = new cc.Vec3();
export class BuiltinSphereShape extends BuiltinShape implements ISphereShape {
set radius (radius: number) {
this.localSphere.radius = radius;
this.collider.node.getWorldScale(_worldScale);
const s = _worldScale.maxAxis();
this.worldSphere.radius = this.localSphere.radius * s;
}
get localSphere () {
return this._localShape as cc.geomUtils.Sphere;
}
get worldSphere () {
return this._worldShape as cc.geomUtils.Sphere;
}
get sphereCollider () {
return this.collider as SphereCollider3D;
}
constructor (radius: number) {
super();
this._localShape = new Sphere(0, 0, 0, radius);
this._worldShape = new Sphere(0, 0, 0, radius);
}
onLoad () {
super.onLoad();
this.radius = this.sphereCollider.radius;
}
}

View File

@@ -0,0 +1,88 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step
*/
export class ArrayCollisionMatrix {
/**
* !#en The matrix storage
* @property matrix
* @type {Array}
*/
public matrix: number[] = [];
/**
* !#en Get an element
* @method get
* @param {Number} i
* @param {Number} j
* @return {Number}
*/
public get (i: number, j: number): number {
if (j > i) {
const temp = j;
j = i;
i = temp;
}
return this.matrix[(i * (i + 1) >> 1) + j - 1];
}
/**
* !#en Set an element
* @method set
* @param {Number} i
* @param {Number} j
* @param {boolean} value
*/
public set (i: number, j: number, value: boolean) {
if (j > i) {
const temp = j;
j = i;
i = temp;
}
this.matrix[(i * (i + 1) >> 1) + j - 1] = value ? 1 : 0;
}
/**
* !#en Sets all elements to zero
* @method reset
*/
public reset () {
for (let i = 0, l = this.matrix.length; i !== l; i++) {
this.matrix[i] = 0;
}
}
/**
* !#en Sets the max number of objects
* @param {Number} n
*/
public setNumObjects (n: number) {
this.matrix.length = n * (n - 1) >> 1;
}
}

View File

@@ -0,0 +1,26 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import '../cocos/instantiate';

View File

@@ -0,0 +1,29 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import '../cannon/instantiate';
import CANNON from '../../../../../external/cannon/cannon';
if (window) window.CANNON = CANNON;

View File

@@ -0,0 +1,26 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
export * from '../framework';

View File

@@ -0,0 +1,117 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const {ccclass, property} = cc._decorator;
const fastRemove = cc.js.array.fastRemove;
const equals = cc.math.equals;
/**
* !#en
* Physics material.
* !#zh
* 物理材质。
* @class PhysicsMaterial
* @extends Asset
*/
@ccclass('cc.PhysicsMaterial')
export class PhysicsMaterial extends cc.Asset {
public static allMaterials: PhysicsMaterial[] = [];
private static _idCounter: number = 0;
@property
private _friction = 0.1;
@property
private _restitution = 0.1;
/**
* !#en
* Friction for this material.
* !#zh
* 物理材质的摩擦力。
* @property {number} friction
*/
@property
get friction () {
return this._friction;
}
set friction (value) {
if (!equals(this._friction, value)) {
this._friction = value;
this.emit('physics_material_update');
}
}
/**
* !#en
* Restitution for this material.
* !#zh
* 物理材质的弹力。
* @property {number} restitution
*/
@property
get restitution () {
return this._restitution;
}
set restitution (value) {
if (!equals(this._restitution, value)) {
this._restitution = value;
this.emit('physics_material_update');
}
}
constructor () {
super();
cc.EventTarget.call(this);
PhysicsMaterial.allMaterials.push(this);
if (this._uuid == '') {
this._uuid = 'pm_' + PhysicsMaterial._idCounter++;
}
}
public clone () {
let c = new PhysicsMaterial();
c._friction = this._friction;
c._restitution = this._restitution;
return c;
}
public destroy (): boolean {
if (super.destroy()) {
fastRemove(PhysicsMaterial.allMaterials, this);
return true;
} else {
return false;
}
}
}
cc.js.mixin(PhysicsMaterial.prototype, cc.EventTarget.prototype);
cc.PhysicsMaterial = PhysicsMaterial;

View File

@@ -0,0 +1,97 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { createBoxShape } from '../../instance';
import { Collider3D } from './collider-component';
import { IBoxShape } from '../../../spec/i-physics-shape';
const {
ccclass,
executeInEditMode,
executionOrder,
menu,
property,
} = cc._decorator;
const Vec3 = cc.Vec3;
/**
* !#en
* Physics box collider
* !#zh
* 物理盒子碰撞器
* @class BoxCollider3D
* @extends Collider3D
*/
@ccclass('cc.BoxCollider3D')
@executionOrder(98)
@menu('i18n:MAIN_MENU.component.physics/Collider/Box 3D')
@executeInEditMode
export class BoxCollider3D extends Collider3D {
/// PUBLIC PROPERTY GETTER\SETTER ///
/**
* !#en
* Get or set the size of the box, in local space.
* !#zh
* 获取或设置盒的大小。
* @property {Vec3} size
*/
@property({
type: cc.Vec3
})
public get size () {
return this._size;
}
public set size (value) {
Vec3.copy(this._size, value);
if (!CC_EDITOR) {
this.boxShape.size = this._size;
}
}
/**
* @property {IBoxShape} boxShape
* @readonly
*/
public get boxShape (): IBoxShape {
return this._shape as IBoxShape;
}
/// PRIVATE PROPERTY ///
@property
private _size: cc.Vec3 = new Vec3(1, 1, 1);
constructor () {
super();
if (!CC_EDITOR) {
this._shape = createBoxShape(this._size);
}
}
}

View File

@@ -0,0 +1,310 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { CollisionCallback, CollisionEventType, TriggerCallback, TriggerEventType, ICollisionEvent } from '../../physics-interface';
import { RigidBody3D } from '../rigid-body-component';
import { PhysicsMaterial } from '../../assets/physics-material';
import { IBaseShape } from '../../../spec/i-physics-shape';
const {ccclass, property} = cc._decorator;
const Vec3 = cc.Vec3;
/**
* !#en
* The base class of the collider.
* !#zh
* 碰撞器的基类。
* @class Collider3D
* @extends Component
* @uses EventTarget
*/
@ccclass('cc.Collider3D')
export class Collider3D extends cc.Component {
/**
* @property {PhysicsMaterial} sharedMaterial
*/
@property({
type: PhysicsMaterial,
displayName: 'Material',
displayOrder: -1
})
public get sharedMaterial () {
return this._material;
}
public set sharedMaterial (value) {
this.material = value;
}
public get material () {
if (!CC_PHYSICS_BUILTIN) {
if (this._isSharedMaterial && this._material != null) {
this._material.off('physics_material_update', this._updateMaterial, this);
this._material = this._material.clone();
this._material.on('physics_material_update', this._updateMaterial, this);
this._isSharedMaterial = false;
}
}
return this._material;
}
public set material (value) {
if (CC_EDITOR || CC_PHYSICS_BUILTIN) {
this._material = value;
return;
}
if (value != null && this._material != null) {
if (this._material._uuid != value._uuid) {
this._material.off('physics_material_update', this._updateMaterial, this);
value.on('physics_material_update', this._updateMaterial, this);
this._isSharedMaterial = false;
this._material = value;
}
} else if (value != null && this._material == null) {
value.on('physics_material_update', this._updateMaterial, this);
this._material = value;
} else if (value == null && this._material != null) {
this._material!.off('physics_material_update', this._updateMaterial, this);
this._material = value;
}
this._updateMaterial();
}
/**
* !#en
* get or set the collider is trigger, this will be always trigger if using builtin.
* !#zh
* 获取或设置碰撞器是否为触发器。
* @property {Boolean} isTrigger
*/
@property({
displayOrder: 0
})
public get isTrigger () {
return this._isTrigger;
}
public set isTrigger (value) {
this._isTrigger = value;
if (!CC_EDITOR) {
this._shape.isTrigger = this._isTrigger;
}
}
/**
* !#en
* get or set the center of the collider, in local space.
* !#zh
* 获取或设置碰撞器的中心点。
* @property {Vec3} center
*/
@property({
type: cc.Vec3,
displayOrder: 1
})
public get center () {
return this._center;
}
public set center (value: cc.Vec3) {
Vec3.copy(this._center, value);
if (!CC_EDITOR) {
this._shape.center = this._center;
}
}
/**
* !#en
* get the collider attached rigidbody, this may be null.
* !#zh
* 获取碰撞器所绑定的刚体组件,可能为 null。
* @property {RigidBody3D|null} attachedRigidbody
* @readonly
*/
public get attachedRigidbody (): RigidBody3D | null {
return this.shape.attachedRigidBody;
}
/**
* !#en
* get collider shape.
* !#zh
* 获取碰撞器形状。
* @property {IBaseShape} shape
* @readonly
*/
public get shape () {
return this._shape;
}
/// PRIVATE PROPERTY ///
protected _shape!: IBaseShape;
protected _isSharedMaterial: boolean = true;
@property({ type: PhysicsMaterial })
protected _material: PhysicsMaterial | null = null;
@property
protected _isTrigger: boolean = false;
@property
protected readonly _center: cc.Vec3 = new Vec3();
protected get _assertOnload (): boolean {
const r = this._isOnLoadCalled == 0;
if (r) { cc.error('Physics Error: Please make sure that the node has been added to the scene'); }
return !r;
}
protected constructor () {
super()
cc.EventTarget.call(this);
}
/// EVENT INTERFACE ///
/**
* !#en
* Register an callback of a specific event type on the EventTarget.
* This type of event should be triggered via `emit`.
* !#zh
* 注册事件目标的特定事件类型回调。这种类型的事件应该被 `emit` 触发。
*
* @method on
* @param {String} type - The type of collider event can be `trigger-enter`, `trigger-stay`, `trigger-exit` or `collision-enter`, `collision-stay`, `collision-exit`.
* @param {Function} callback - The callback that will be invoked when the event is dispatched.
* The callback is ignored if it is a duplicate (the callbacks are unique).
* @param {ITriggerEvent|ICollisionEvent} callback.event Callback function argument
* @param {Object} [target] - The target (this object) to invoke the callback, can be null.
* @return {Function} - Just returns the incoming callback so you can save the anonymous function easier.
* @typescript
* on<T extends Function>(type: string, callback: T, target?: any, useCapture?: boolean): T
* @example
* eventTarget.on('fire', function (event) {
* // event is ITriggerEvent or ICollisionEvent
* }, node);
*/
public on (type: TriggerEventType | CollisionEventType, callback: TriggerCallback | CollisionCallback, target?: Object, useCapture?: any): any {
}
/**
* !#en
* Removes the listeners previously registered with the same type, callback, target and or useCapture,
* if only type is passed as parameter, all listeners registered with that type will be removed.
* !#zh
* 删除之前用同类型,回调,目标或 useCapture 注册的事件监听器,如果只传递 type将会删除 type 类型的所有事件监听器。
*
* @method off
* @param {String} type - The type of collider event can be `trigger-enter`, `trigger-stay`, `trigger-exit` or `collision-enter`, `collision-stay`, `collision-exit`.
* @param {Function} [callback] - The callback to remove.
* @param {Object} [target] - The target (this object) to invoke the callback, if it's not given, only callback without target will be removed.
* @example
* // register fire eventListener
* var callback = eventTarget.on('fire', function () {
* cc.log("fire in the hole");
* }, target);
* // remove fire event listener
* eventTarget.off('fire', callback, target);
* // remove all fire event listeners
* eventTarget.off('fire');
*/
public off (type: TriggerEventType | CollisionEventType, callback: TriggerCallback | CollisionCallback, target?: any) {
}
/**
* !#en
* Register an callback of a specific event type on the EventTarget,
* the callback will remove itself after the first time it is triggered.
* !#zh
* 注册事件目标的特定事件类型回调,回调会在第一时间被触发后删除自身。
*
* @method once
* @param {String} type - The type of collider event can be `trigger-enter`, `trigger-stay`, `trigger-exit` or `collision-enter`, `collision-stay`, `collision-exit`.
* @param {Function} callback - The callback that will be invoked when the event is dispatched.
* The callback is ignored if it is a duplicate (the callbacks are unique).
* @param {ITriggerEvent|ICollisionEvent} callback.event callback function argument.
* @param {Object} [target] - The target (this object) to invoke the callback, can be null.
* @example
* eventTarget.once('fire', function (event) {
* // event is ITriggerEvent or ICollisionEvent
* }, node);
*/
public once (type: TriggerEventType | CollisionEventType, callback: TriggerCallback | CollisionCallback, target?: Object) {
}
/* declare for typescript tip */
public emit (key: TriggerEventType | CollisionEventType, ...args: any[]): void {
}
/// COMPONENT LIFECYCLE ///
protected __preload () {
if (!CC_EDITOR) {
this._shape.__preload!(this);
}
}
protected onLoad () {
if (!CC_EDITOR) {
if (!CC_PHYSICS_BUILTIN) {
this.sharedMaterial = this._material == null ? cc.director.getPhysics3DManager().defaultMaterial : this._material;
}
this._shape.onLoad!();
}
}
protected onEnable () {
if (!CC_EDITOR) {
this._shape.onEnable!();
}
}
protected onDisable () {
if (!CC_EDITOR) {
this._shape.onDisable!();
}
}
protected onDestroy () {
if (!CC_EDITOR) {
if (this._material) {
this._material.off('physics_material_update', this._updateMaterial, this);
}
this._shape.onDestroy!();
}
}
private _updateMaterial () {
if (!CC_EDITOR) {
this._shape.material = this._material;
}
}
}
cc.js.mixin(Collider3D.prototype, cc.EventTarget.prototype);

View File

@@ -0,0 +1,91 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { createSphereShape } from '../../instance';
import { Collider3D } from './collider-component';
import { ISphereShape } from '../../../spec/i-physics-shape';
const {
ccclass,
executeInEditMode,
executionOrder,
menu,
property,
} = cc._decorator;
/**
* !#en
* Physics sphere collider
* !#zh
* 物理球碰撞器
* @class SphereCollider3D
* @extends Collider3D
*/
@ccclass('cc.SphereCollider3D')
@executionOrder(98)
@menu('i18n:MAIN_MENU.component.physics/Collider/Sphere 3D')
@executeInEditMode
export class SphereCollider3D extends Collider3D {
/// PUBLIC PROPERTY GETTER\SETTER ///
/**
* !#en
* Get or set the radius of the sphere.
* !#zh
* 获取或设置球的半径。
* @property {number} radius
*/
@property
public get radius () {
return this._radius;
}
public set radius (value) {
this._radius = value;
if (!CC_EDITOR) {
this.sphereShape.radius = this._radius;
}
}
/**
* @property {ISphereShape} sphereShape
*/
public get sphereShape (): ISphereShape {
return this._shape as ISphereShape;
}
/// PRIVATE PROPERTY ///
@property
private _radius: number = 0.5;
constructor () {
super();
if (!CC_EDITOR) {
this._shape = createSphereShape(this._radius);
}
}
}

View File

@@ -0,0 +1,188 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { RigidBody3D } from './rigid-body-component';
const {
ccclass,
executeInEditMode,
executionOrder,
menu,
property,
requireComponent,
disallowMultiple,
} = cc._decorator;
const Vec3 = cc.Vec3;
/**
* !#en
* Each frame applies a constant force to a rigid body, depending on the RigidBody3D
* !#zh
* 在每帧对一个刚体施加持续的力,依赖 RigidBody3D 组件
* @class ConstantForce
* @extends Component
*/
@ccclass('cc.ConstantForce')
@executionOrder(98)
@requireComponent(RigidBody3D)
@menu('i18n:MAIN_MENU.component.physics/Constant Force 3D')
@disallowMultiple
@executeInEditMode
export class ConstantForce extends cc.Component {
private _rigidbody: RigidBody3D | null = null;
@property
private readonly _force: cc.Vec3 = new Vec3();
@property
private readonly _localForce: cc.Vec3 = new Vec3();
@property
private readonly _torque: cc.Vec3 = new Vec3();
@property
private readonly _localTorque: cc.Vec3 = new Vec3();
private _mask: number = 0;
/**
* !#en
* Set the force used in the world coordinate system, use `this.force = otherVec3`.
* !#zh
* 设置世界坐标系中使用的力,设置时请用 `this.force = otherVec3` 的方式。
* @property {Vec3} force
*/
@property({
displayOrder: 0
})
public get force () {
return this._force;
}
public set force (value: cc.Vec3) {
Vec3.copy(this._force, value);
this._maskUpdate(this._force, 1);
}
/**
* !#en
* Set the force used in the local coordinate system, using `this.localforce = otherVec3`.
* !#zh
* 获取和设置本地坐标系中使用的力,设置时请用 `this.localForce = otherVec3` 的方式。
* @property {Vec3} localForce
*/
@property({
displayOrder: 1
})
public get localForce () {
return this._localForce;
}
public set localForce (value: cc.Vec3) {
Vec3.copy(this._localForce, value);
this._maskUpdate(this.localForce, 2);
}
/**
* !#en
* Torque applied to the world orientation
* !#zh
* 对世界朝向施加的扭矩
* @note
* 设置时请用 this.torque = otherVec3 的方式
* @property {Vec3} torque
*/
@property({
displayOrder: 2
})
public get torque () {
return this._torque;
}
public set torque (value: cc.Vec3) {
Vec3.copy(this._torque, value);
this._maskUpdate(this._torque, 4);
}
/**
* !#en
* Torque applied to local orientation, using `this.localtorque = otherVec3`.
* !#zh
* 对本地朝向施加的扭矩,设置时请用 `this.localTorque = otherVec3` 的方式。
* @property {Vec3} localTorque
*/
@property({
displayOrder: 3
})
public get localTorque () {
return this._localTorque;
}
public set localTorque (value: cc.Vec3) {
Vec3.copy(this._localTorque, value);
this._maskUpdate(this._localTorque, 8);
}
public onLoad () {
if (!CC_PHYSICS_BUILTIN) {
this._rigidbody = this.node.getComponent(RigidBody3D);
this._maskUpdate(this._force, 1);
this._maskUpdate(this._localForce, 2);
this._maskUpdate(this._torque, 4);
this._maskUpdate(this._localTorque, 8);
}
}
public lateUpdate (dt: number) {
if (!CC_PHYSICS_BUILTIN) {
if (this._rigidbody != null && this._mask != 0) {
if (this._mask & 1) {
this._rigidbody.applyForce(this._force);
}
if (this._mask & 2) {
this._rigidbody.applyLocalForce(this.localForce);
}
if (this._mask & 4) {
this._rigidbody.applyTorque(this._torque);
}
if (this._mask & 8) {
this._rigidbody.applyLocalTorque(this._localTorque);
}
}
}
}
private _maskUpdate (t: cc.Vec3, m: number) {
if (Vec3.strictEquals(t, Vec3.ZERO)) {
this._mask &= ~m;
} else {
this._mask |= m;
}
}
}

View File

@@ -0,0 +1,540 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { IRigidBody } from '../../spec/I-rigid-body';
import { createRigidBody } from '../instance';
const {
ccclass,
disallowMultiple,
executeInEditMode,
executionOrder,
menu,
property,
} = cc._decorator;
const Vec3 = cc.Vec3;
/**
* !#en
* RigidBody is the basic object that make up the physical world, and it can make a node physically affected and react.
* !#zh
* 刚体是组成物理世界的基本对象,可以让一个节点受到物理影响并产生反应。该组件在使用 Builtin 物理引擎时无效。
* @class RigidBody3D
* @extends Component
*/
@ccclass('cc.RigidBody3D')
@executionOrder(99)
@menu('i18n:MAIN_MENU.component.physics/Rigid Body 3D')
@executeInEditMode
@disallowMultiple
export class RigidBody3D extends cc.Component {
/// PUBLIC PROPERTY GETTER\SETTER ///
/**
* !#en
* Whether sleep is allowed.
* !#zh
* 是否允许休眠。
* @property {boolean} allowSleep
*/
public get allowSleep (): boolean {
return this._allowSleep;
}
public set allowSleep (v: boolean) {
this._allowSleep = v;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.allowSleep = v;
}
}
/**
* !#en
* The mass of the rigidbody.
* !#zh
* 刚体的质量。
* @property {number} mass
*/
@property({
displayOrder: 0
})
public get mass () {
return this._mass;
}
public set mass (value) {
this._mass = value;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.mass = value;
}
}
/**
* !#en
* Used to reduce the linear rate of rigidbody. The larger the value, the slower the rigidbody moves.
* !#zh
* 线性阻尼,用于减小刚体的线性速率,值越大物体移动越慢。
* @property {number} linearDamping
*/
@property({
displayOrder: 1
})
public get linearDamping () {
return this._linearDamping;
}
public set linearDamping (value) {
this._linearDamping = value;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.linearDamping = value;
}
}
/**
* !#en
* Used to reduce the rotation rate of rigidbody. The larger the value, the slower the rigidbody rotates.
* !#zh
* 角阻尼,用于减小刚体的旋转速率,值越大刚体旋转越慢。
* @property {number} angularDamping
*/
@property({
displayOrder: 2
})
public get angularDamping () {
return this._angularDamping;
}
public set angularDamping (value) {
this._angularDamping = value;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.angularDamping = value;
}
}
/**
* !#en
* If enabled, the developer controls the displacement and rotation of the rigidbody, not the physics engine.
* !#zh
* 是否由开发者来控制刚体的位移和旋转,而不是受物理引擎的影响。
* @property {boolean} isKinematic
*/
@property({
displayOrder: 3
})
public get isKinematic () {
return this._isKinematic;
}
public set isKinematic (value) {
this._isKinematic = value;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.isKinematic = value;
}
}
/**
* !#en
* If enabled, the rigidbody is affected by gravity.
* !#zh
* 如果开启,刚体会受到重力影响。
* @property {boolean} useGravity
*/
@property({
displayOrder: 4
})
public get useGravity () {
return this._useGravity;
}
public set useGravity (value) {
this._useGravity = value;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.useGravity = value;
}
}
/**
* !#en
* If enabled, the rigidbody will be fixed without rotation during a collision.
* !#zh
* 如果开启,发生碰撞时会固定刚体不产生旋转。
* @property {boolean} fixedRotation
*/
@property({
displayOrder: 5
})
public get fixedRotation () {
return this._fixedRotation;
}
public set fixedRotation (value) {
this._fixedRotation = value;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.fixedRotation = value;
}
}
/**
* !#en
* It can affect the linear velocity change of the rigidbody in each axis. The larger the value, the faster the rigidbody moves.
* !#zh
* 线性因子,可影响刚体在每个轴向的线性速度变化,值越大刚体移动越快。
* @property {Vec3} linearFactor
*/
@property({
displayOrder: 6
})
public get linearFactor (): cc.Vec3 {
return this._linearFactor;
}
public set linearFactor (value: cc.Vec3) {
Vec3.copy(this._linearFactor, value);
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.linearFactor = this._linearFactor;
}
}
/**
* !#en
* It can affect the rotation speed change of the rigidbody in each axis. The larger the value, the faster the rigidbody rotates.
* !#zh
* 旋转因子,可影响刚体在每个轴向的旋转速度变化,值越大刚体旋转越快。
* @property {Vec3} angularFactor
*/
@property({
displayOrder: 7
})
public get angularFactor () {
return this._angularFactor;
}
public set angularFactor (value: cc.Vec3) {
Vec3.copy(this._angularFactor, value);
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.angularFactor = this._angularFactor;
}
}
/**
* !#en
* The rigidbody is awake.
* !#zh
* 刚体是否为唤醒的状态。
* @property {boolean} isAwake
* @readonly
*/
public get isAwake (): boolean {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
return this._body.isAwake;
}
return false;
}
/**
* !#en
* The rigidbody can enter hibernation.
* !#zh
* 刚体是否为可进入休眠的状态。
* @property {boolean} isSleepy
* @readonly
*/
public get isSleepy (): boolean {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
return this._body.isSleepy;
}
return false;
}
/**
* !#en
* The rigidbody is sleeping.
* !#zh
* 刚体是否为正在休眠的状态。
* @property {boolean} isSleeping
* @readonly
*/
public get isSleeping (): boolean {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
return this._body.isSleeping;
}
return false;
}
/**
* !#en
* Get the rigidbody object inside the physics engine.
* !#zh
* 获得物理引擎内部刚体对象。
* @property {IRigidBody} rigidBody
* @readonly
*/
public get rigidBody () {
return this._body;
}
private _body!: IRigidBody;
/// PRIVATE PROPERTY ///
// @property
private _allowSleep: boolean = true;
@property
private _mass: number = 10;
@property
private _linearDamping: number = 0.1;
@property
private _angularDamping: number = 0.1;
@property
private _fixedRotation: boolean = false;
@property
private _isKinematic: boolean = false;
@property
private _useGravity: boolean = true;
@property
private _linearFactor: cc.Vec3 = new Vec3(1, 1, 1);
@property
private _angularFactor: cc.Vec3 = new Vec3(1, 1, 1);
protected get _assertOnload (): boolean {
const r = this._isOnLoadCalled == 0;
if (r) { cc.error('Physics Error: Please make sure that the node has been added to the scene'); }
return !r;
}
constructor () {
super();
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body = createRigidBody();
}
}
/// COMPONENT LIFECYCLE ///
protected __preload () {
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.__preload!(this);
}
}
protected onEnable () {
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.onEnable!();
}
}
protected onDisable () {
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.onDisable!();
}
}
protected onDestroy () {
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.onDestroy!();
}
}
/// PUBLIC METHOD ///
/**
* !#en
* A force is applied to a rigid body at a point in world space.
* !#zh
* 在世界空间中的某点上对刚体施加一个作用力。
* @method applyForce
* @param {Vec3} force
* @param {Vec3} relativePoint The point of action, relative to the center of the rigid body.
*/
public applyForce (force: cc.Vec3, relativePoint?: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.applyForce(force, relativePoint);
}
}
/**
* !#en
* Apply a force on the rigid body at a point in local space.
* !#zh
* 在本地空间中的某点上对刚体施加一个作用力。
* @method applyLocalForce
* @param {Vec3} force
* @param {Vec3} localPoint Point of application
*/
public applyLocalForce (force: cc.Vec3, localPoint?: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.applyLocalForce(force, localPoint);
}
}
/**
* !#en
* Apply an impulse to a rigid body at a point in world space.
* !#zh
* 在世界空间的某点上对刚体施加一个冲量。
* @method applyImpulse
* @param {Vec3} impulse
* @param {Vec3} relativePoint The point of action, relative to the center of the rigid body.
*/
public applyImpulse (impulse: cc.Vec3, relativePoint?: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.applyImpulse(impulse, relativePoint);
}
}
/**
* !#en
* Apply an impulse to the rigid body at a point in local space.
* !#zh
* 在本地空间的某点上对刚体施加一个冲量。
* @method applyLocalImpulse
* @param {Vec3} impulse
* @param {Vec3} localPoint Point of application
*/
public applyLocalImpulse (impulse: cc.Vec3, localPoint?: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.applyLocalImpulse(impulse, localPoint);
}
}
/**
* !#en
* Apply a torque to the rigid body.
* !#zh
* 对刚体施加扭转力。
* @method applyTorque
* @param {Vec3} torque
*/
public applyTorque (torque: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.applyTorque(torque);
}
}
/**
* !#en
* Apply a local torque to the rigid body.
* !#zh
* 对刚体施加本地扭转力。
* @method applyLocalTorque
* @param {Vec3} torque
*/
public applyLocalTorque (torque: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.applyLocalTorque(torque);
}
}
/**
* !#en
* Awaken the rigid body.
* !#zh
* 唤醒刚体。
* @method wakeUp
*/
public wakeUp () {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.wakeUp();
}
}
/**
* !#en
* Dormant rigid body.
* !#zh
* 休眠刚体。
* @method sleep
*/
public sleep () {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.sleep();
}
}
/**
* !#en
* Get linear velocity.
* !#zh
* 获取线性速度。
* @method getLinearVelocity
* @param {Vec3} out
*/
public getLinearVelocity (out: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.getLinearVelocity(out);
}
}
/**
* !#en
* Set linear speed.
* !#zh
* 设置线性速度。
* @method setLinearVelocity
* @param {Vec3} value
*/
public setLinearVelocity (value: cc.Vec3): void {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.setLinearVelocity(value);
}
}
/**
* !#en
* Gets the rotation speed.
* !#zh
* 获取旋转速度。
* @method getAngularVelocity
* @param {Vec3} out
*/
public getAngularVelocity (out: cc.Vec3) {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.getAngularVelocity(out);
}
}
/**
* !#en
* Set rotation speed.
* !#zh
* 设置旋转速度。
* @method setAngularVelocity
* @param {Vec3} value
*/
public setAngularVelocity (value: cc.Vec3): void {
if (this._assertOnload && !CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this._body.setAngularVelocity(value);
}
}
}

View File

@@ -0,0 +1,52 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { Physics3DManager } from './physics-manager';
import { PhysicsRayResult } from './physics-ray-result';
import { BoxCollider3D } from './components/collider/box-collider-component';
import { Collider3D } from './components/collider/collider-component';
import { SphereCollider3D } from './components/collider/sphere-collider-component';
import { RigidBody3D } from './components/rigid-body-component';
import { ConstantForce } from './components/constant-force';
import { PhysicsMaterial } from './assets/physics-material';
export {
Physics3DManager,
PhysicsRayResult,
PhysicsMaterial,
Collider3D,
BoxCollider3D,
SphereCollider3D,
RigidBody3D,
};
cc.Physics3DManager = Physics3DManager;
cc.Collider3D = Collider3D;
cc.BoxCollider3D = BoxCollider3D;
cc.SphereCollider3D = SphereCollider3D;
cc.RigidBody3D = RigidBody3D;
cc.PhysicsRayResult = PhysicsRayResult;
cc.ConstantForce = ConstantForce;

View File

@@ -0,0 +1,45 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { BoxShape, PhysicsWorld, RigidBody, SphereShape } from './physics-selector';
import { IRigidBody } from '../spec/I-rigid-body';
import { IBoxShape, ISphereShape } from '../spec/i-physics-shape';
import { IPhysicsWorld } from '../spec/i-physics-world';
export function createPhysicsWorld (): IPhysicsWorld {
return new PhysicsWorld() as IPhysicsWorld;
}
export function createRigidBody (): IRigidBody {
return new RigidBody!() as IRigidBody;
}
export function createBoxShape (size: cc.Vec3): IBoxShape {
return new BoxShape(size) as IBoxShape;
}
export function createSphereShape (radius: number): ISphereShape {
return new SphereShape(radius) as ISphereShape;
}

View File

@@ -0,0 +1,44 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* !#en The rigid body type
* !#zh 刚体类型
* @enum ERigidBodyType
*/
export enum ERigidBodyType {
/**
* @property {Number} DYNAMIC
*/
DYNAMIC = 1,
/**
* @property {Number} STATIC
*/
STATIC = 2,
/**
* @property {Number} KINEMATIC
*/
KINEMATIC = 4,
}

View File

@@ -0,0 +1,185 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { Collider3D } from './components/collider/collider-component';
/**
* !#en
* Trigger event
* !#zh
* 触发事件。
* @class ITriggerEvent
*/
export interface ITriggerEvent {
/**
* !#en
* The type of event fired
* !#zh
* 触发的事件类型
* @property {String} type
* @readonly
*/
readonly type: TriggerEventType;
/**
* !#en
* Triggers its own collider in the event
* !#zh
* 触发事件中的自己的碰撞器
* @property {Collider3D} selfCollider
* @readonly
*/
readonly selfCollider: Collider3D;
/**
* !#en
* Triggers another collider in the event
* !#zh
* 触发事件中的另一个碰撞器
* @property {Collider3D} otherCollider
* @readonly
*/
readonly otherCollider: Collider3D;
}
/**
* !#en
* The value type definition of the trigger event.
* !#zh
* 触发事件的值类型定义。
*/
export type TriggerEventType = 'trigger-enter' | 'trigger-stay' | 'trigger-exit';
/**
* !#en
* The callback signature definition of the event that was fired.
* !#zh
* 触发事件的回调函数签名定义。
*/
export type TriggerCallback = (event: ITriggerEvent) => void;
/**
* !#en
* Collision information for collision events.
* !#zh
* 碰撞事件的碰撞信息。
* @class IContactEquation
*/
export interface IContactEquation {
/**
* !#en
* The collision point A in the collision information.
* !#zh
* 碰撞信息中的碰撞点 A。
* @property {Vec3} contactA
* @readonly
*/
readonly contactA: cc.Vec3;
/**
* !#en
* Collision point B in collision information.
* !#zh
* 碰撞信息中的碰撞点 B。
* @property {Vec3} contactB
* @readonly
*/
readonly contactB: cc.Vec3;
/**
* !#en
* Normals in collision information.
* !#zh
* 碰撞信息中的法线。
* @property {Vec3} normal
* @readonly
*/
readonly normal: cc.Vec3;
}
/**
* !#en
* Collision events.
* !#zh
* 碰撞事件。
* @class ICollisionEvent
*/
export interface ICollisionEvent {
/**
* !#en
* Event type of collision.
* !#zh
* 碰撞的事件类型。
* @property {String} type
* @readonly
*/
readonly type: CollisionEventType;
/**
* !#en
* Collider of its own in collision.
* !#zh
* 碰撞中的自己的碰撞器。
* @property {Collider3D} selfCollider
* @readonly
*/
readonly selfCollider: Collider3D;
/**
* !#en
* Another collider in the collision.
* !#zh
* 碰撞中的另一个碰撞器。
* @property {Collider3D} otherCollider
* @readonly
*/
readonly otherCollider: Collider3D;
/**
* !#en
* Information about all the points of impact in the collision.
* !#zh
* 碰撞中的所有碰撞点的信息。
* @property {IContactEquation[]} contacts
* @readonly
*/
readonly contacts: IContactEquation[];
}
/**
* !#en
* Value type definition for collision events.
* !#zh
* 碰撞事件的值类型定义。
*/
export type CollisionEventType = 'collision-enter' | 'collision-stay' | 'collision-exit';
/**
* !#en
* The callback signature definition for the collision event.
* !#zh
* 碰撞事件的回调函数签名定义。
*/
export type CollisionCallback = (event: ICollisionEvent) => void;

View File

@@ -0,0 +1,313 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { IPhysicsWorld, IRaycastOptions } from '../spec/i-physics-world';
import { createPhysicsWorld } from './instance';
import { PhysicsMaterial } from './assets/physics-material';
import { PhysicsRayResult } from './physics-ray-result';
const { property, ccclass } = cc._decorator;
/**
* !#en
* Physical systems manager.
* !#zh
* 物理系统管理器。
* @class Physics3DManager
*/
@ccclass("cc.Physics3DManager")
export class Physics3DManager {
/**
* !#en
* Whether to enable the physics system, default is false.
* !#zh
* 是否启用物理系统,默认不启用。
* @property {boolean} enabled
*/
get enabled (): boolean {
return this._enabled;
}
set enabled (value: boolean) {
this._enabled = value;
}
/**
* !#en
* Whether to allow the physics system to automatically hibernate, default is true.
* !#zh
* 物理系统是否允许自动休眠,默认为 true。
* @property {boolean} allowSleep
*/
get allowSleep (): boolean {
return this._allowSleep;
}
set allowSleep (v: boolean) {
this._allowSleep = v;
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this.physicsWorld.allowSleep = this._allowSleep;
}
}
/**
* !#en
* The maximum number of sub-steps a full step is permitted to be broken into, default is 2.
* !#zh
* 物理每帧模拟的最大子步数,默认为 2。
* @property {number} maxSubStep
*/
get maxSubStep (): number {
return this._maxSubStep;
}
set maxSubStep (value: number) {
this._maxSubStep = value;
}
/**
* !#en
* Time spent in each simulation of physics, default is 1/60s.
* !#zh
* 物理每步模拟消耗的固定时间,默认为 1/60 秒。
* @property {number} deltaTime
*/
get deltaTime (): number {
return this._fixedTime;
}
set deltaTime (value: number) {
this._fixedTime = value;
}
/**
* !#en
* Whether to use a fixed time step.
* !#zh
* 是否使用固定的时间步长。
* @property {boolean} useFixedTime
*/
get useFixedTime (): boolean {
return this._useFixedTime;
}
set useFixedTime (value: boolean) {
this._useFixedTime = value;
}
/**
* !#en
* Gravity value of the physics simulation, default is (0, -10, 0).
* !#zh
* 物理世界的重力数值,默认为 (0, -10, 0)。
* @property {Vec3} gravity
*/
get gravity (): cc.Vec3 {
return this._gravity;
}
set gravity (gravity: cc.Vec3) {
this._gravity.set(gravity);
if (!CC_EDITOR && !CC_PHYSICS_BUILTIN) {
this.physicsWorld.gravity = gravity;
}
}
/**
* !#en
* Gets the global default physical material. Note that builtin is null.
* !#zh
* 获取全局的默认物理材质注意builtin 时为 null。
* @property {PhysicsMaterial | null} defaultMaterial
* @readonly
*/
get defaultMaterial (): PhysicsMaterial | null {
return this._material;
}
readonly physicsWorld: IPhysicsWorld;
readonly raycastClosestResult = new PhysicsRayResult();
readonly raycastResults: PhysicsRayResult[] = [];
@property
private _enabled = false;
@property
private _allowSleep = true;
@property
private readonly _gravity = new cc.Vec3(0, -10, 0);
@property
private _maxSubStep = 1;
@property
private _fixedTime = 1.0 / 60.0;
@property
private _useFixedTime = true;
useAccumulator = false;
private _accumulator = 0;
useFixedDigit = false;
useInternalTime = false;
readonly fixDigits = {
position: 5,
rotation: 12,
timeNow: 3,
}
private _deltaTime = 0;
private _lastTime = 0;
private readonly _material: cc.PhysicsMaterial | null = null;
private readonly raycastOptions: IRaycastOptions = {
'groupIndex': -1,
'queryTrigger': true,
'maxDistance': Infinity
}
private readonly raycastResultPool = new cc.RecyclePool(() => {
return new PhysicsRayResult();
}, 1);
private constructor () {
cc.director._scheduler && cc.director._scheduler.enableForTarget(this);
this.physicsWorld = createPhysicsWorld();
this._lastTime = performance.now();
if (!CC_PHYSICS_BUILTIN) {
this.gravity = this._gravity;
this.allowSleep = this._allowSleep;
this._material = new PhysicsMaterial();
this._material.friction = 0.1;
this._material.restitution = 0.1;
this._material.on('physics_material_update', this._updateMaterial, this);
this.physicsWorld.defaultMaterial = this._material;
}
}
/**
* !#en
* A physical system simulation is performed once and will now be performed automatically once per frame.
* !#zh
* 执行一次物理系统的模拟,目前将在每帧自动执行一次。
* @method update
* @param {number} deltaTime The time difference from the last execution is currently elapsed per frame
*/
update (deltaTime: number) {
if (CC_EDITOR) {
return;
}
if (!this._enabled) {
return;
}
if (this.useInternalTime) {
var now = parseFloat(performance.now().toFixed(this.fixDigits.timeNow));
this._deltaTime = now > this._lastTime ? (now - this._lastTime) / 1000 : 0;
this._lastTime = now;
} else {
this._deltaTime = deltaTime;
}
cc.director.emit(cc.Director.EVENT_BEFORE_PHYSICS);
if (CC_PHYSICS_BUILTIN) {
this.physicsWorld.step(this._fixedTime);
} else {
if (this._useFixedTime) {
this.physicsWorld.step(this._fixedTime);
} else {
if (this.useAccumulator) {
let i = 0;
this._accumulator += this._deltaTime;
while (i < this._maxSubStep && this._accumulator > this._fixedTime) {
this.physicsWorld.step(this._fixedTime);
this._accumulator -= this._fixedTime;
i++;
}
} else {
this.physicsWorld.step(this._fixedTime, this._deltaTime, this._maxSubStep);
}
}
}
cc.director.emit(cc.Director.EVENT_AFTER_PHYSICS);
}
/**
* !#en Detect all collision boxes and return all detected results, or null if none is detected. Note that the return value is taken from the object pool, so do not save the result reference or modify the result.
* !#zh 检测所有的碰撞盒,并返回所有被检测到的结果,若没有检测到,则返回空值。注意返回值是从对象池中取的,所以请不要保存结果引用或者修改结果。
* @method raycast
* @param {Ray} worldRay A ray in world space
* @param {number|string} groupIndexOrName Collision group index or group name
* @param {number} maxDistance Maximum detection distance
* @param {boolean} queryTrigger Detect trigger or not
* @return {PhysicsRayResult[] | null} Detected result
*/
raycast (worldRay: cc.geomUtils.Ray, groupIndexOrName: number | string = 0, maxDistance = Infinity, queryTrigger = true): PhysicsRayResult[] | null {
this.raycastResultPool.reset();
this.raycastResults.length = 0;
if (typeof groupIndexOrName == "string") {
let groupIndex = cc.game.groupList.indexOf(groupIndexOrName);
if (groupIndex == -1) groupIndex = 0;
this.raycastOptions.groupIndex = groupIndex;
} else {
this.raycastOptions.groupIndex = groupIndexOrName;
}
this.raycastOptions.maxDistance = maxDistance;
this.raycastOptions.queryTrigger = queryTrigger;
let result = this.physicsWorld.raycast(worldRay, this.raycastOptions, this.raycastResultPool, this.raycastResults);
if (result) return this.raycastResults;
return null;
}
/**
* !#en Detect all collision boxes and return the detection result with the shortest ray distance. If not, return null value. Note that the return value is taken from the object pool, so do not save the result reference or modify the result.
* !#zh 检测所有的碰撞盒,并返回射线距离最短的检测结果,若没有,则返回空值。注意返回值是从对象池中取的,所以请不要保存结果引用或者修改结果。
* @method raycastClosest
* @param {Ray} worldRay A ray in world space
* @param {number|string} groupIndexOrName Collision group index or group name
* @param {number} maxDistance Maximum detection distance
* @param {boolean} queryTrigger Detect trigger or not
* @return {PhysicsRayResult|null} Detected result
*/
raycastClosest (worldRay: cc.geomUtils.Ray, groupIndexOrName: number | string = 0, maxDistance = Infinity, queryTrigger = true): PhysicsRayResult | null {
if (typeof groupIndexOrName == "string") {
let groupIndex = cc.game.groupList.indexOf(groupIndexOrName);
if (groupIndex == -1) groupIndex = 0;
this.raycastOptions.groupIndex = groupIndex;
} else {
this.raycastOptions.groupIndex = groupIndexOrName;
}
this.raycastOptions.maxDistance = maxDistance;
this.raycastOptions.queryTrigger = queryTrigger;
let result = this.physicsWorld.raycastClosest(worldRay, this.raycastOptions, this.raycastClosestResult);
if (result) return this.raycastClosestResult;
return null;
}
private _updateMaterial () {
if (!CC_PHYSICS_BUILTIN) {
this.physicsWorld.defaultMaterial = this._material;
}
}
}

View File

@@ -0,0 +1,108 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { Collider3D } from '../exports/physics-framework';
const Vec3 = cc.Vec3;
/**
* !#en
* Used to store physical ray detection results
* !#zh
* 用于保存物理射线检测结果
* @class PhysicsRayResult
*/
export class PhysicsRayResult {
/**
* !#en
* Hit the point
* !#zh
* 击中点
* @property {Vec3} hitPoint
* @readonly
*/
get hitPoint (): cc.Vec3 {
return this._hitPoint;
}
/**
* !#en
* Distance
* !#zh
* 距离
* @property {number} distance
* @readonly
*/
get distance (): number {
return this._distance;
}
/**
* !#en
* Hit the collision box
* !#zh
* 击中的碰撞盒
* @property {Collider3D} collider
* @readonly
*/
get collider (): Collider3D {
return this._collidier!;
}
private _hitPoint: cc.Vec3 = new Vec3();
private _distance: number = 0;
private _collidier: Collider3D | null = null;
/**
* !#en
* Set up ray. This method is used internally by the engine. Do not call it from an external script
* !#zh
* 设置射线,此方法由引擎内部使用,请勿在外部脚本调用
* @method _assign
* @param {Vec3} hitPoint
* @param {number} distance
* @param {Collider3D} collider
*/
public _assign (hitPoint: cc.Vec3, distance: number, collider: Collider3D) {
Vec3.copy(this._hitPoint, hitPoint);
this._distance = distance;
this._collidier = collider;
}
/**
* !#en
* Clone
* !#zh
* 克隆
* @method clone
*/
public clone () {
const c = new PhysicsRayResult();
Vec3.copy(c._hitPoint, this._hitPoint);
c._distance = this._distance;
c._collidier = this._collidier;
return c;
}
}

View File

@@ -0,0 +1,51 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
// Cannon
import { CannonRigidBody } from '../cannon/cannon-rigid-body';
import { CannonWorld } from '../cannon/cannon-world';
import { CannonBoxShape } from '../cannon/shapes/cannon-box-shape';
import { CannonSphereShape } from '../cannon/shapes/cannon-sphere-shape';
// built-in
import { BuiltInWorld } from '../cocos/builtin-world';
import { BuiltinBoxShape } from '../cocos/shapes/builtin-box-shape';
import { BuiltinSphereShape } from '../cocos/shapes/builtin-sphere-shape';
export let BoxShape: typeof CannonBoxShape | typeof BuiltinBoxShape;
export let SphereShape: typeof CannonSphereShape | typeof BuiltinSphereShape;
export let RigidBody: typeof CannonRigidBody | null;
export let PhysicsWorld: typeof CannonWorld | typeof BuiltInWorld;
export function instantiate (
boxShape: typeof BoxShape,
sphereShape: typeof SphereShape,
body: typeof RigidBody,
world: typeof PhysicsWorld) {
BoxShape = boxShape;
SphereShape = sphereShape;
RigidBody = body;
PhysicsWorld = world;
}

Some files were not shown because too many files have changed in this diff Show More