Skip to content

Instantly share code, notes, and snippets.

@jpsarda
Last active July 23, 2017 16:36
Show Gist options
  • Save jpsarda/6221653 to your computer and use it in GitHub Desktop.
Save jpsarda/6221653 to your computer and use it in GitHub Desktop.
FScrollContainer for Unity3D Futile 2D game engine. To test it you need to change a few things in the Futile code : * _theSingleTouchable must be protected and not private * Update() must be declared virtual.
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/*
A serie of 4 classes extending Futile classes in order to manage scrollable containers with scrollable buttons.
Use :
public void TestScrollContainer()
{
// Create a vertically scrollable container of width 30, height 200, and scrollable height 600
FScrollContainer scroll=new FScrollContainer(30,200);
scroll.SetContentSize(30,600);
AddChild(scroll);
//Add 4 sprites with different colors in the contentContainer (different colors in order to see them scrolling)
FSprite scrollSprite;
scrollSprite=new FSprite("Futile_White");
scrollSprite.color=new Color(1f,0f,0f);
scrollSprite.scaleX=30f/scrollSprite.textureRect.width;
scrollSprite.scaleY=150f/scrollSprite.textureRect.height;
scrollSprite.y=-300f+75f;
scroll.contentContainer.AddChild(scrollSprite);
scrollSprite=new FSprite("Futile_White");
scrollSprite.color=new Color(0f,1f,0f);
scrollSprite.scaleX=30f/scrollSprite.textureRect.width;
scrollSprite.scaleY=150f/scrollSprite.textureRect.height;
scrollSprite.y=-300f+150f+75f;
scroll.contentContainer.AddChild(scrollSprite);
scrollSprite=new FSprite("Futile_White");
scrollSprite.color=new Color(1f,0f,0f);
scrollSprite.scaleX=30f/scrollSprite.textureRect.width;
scrollSprite.scaleY=150f/scrollSprite.textureRect.height;
scrollSprite.y=-300f+150f*2+75f;
scroll.contentContainer.AddChild(scrollSprite);
scrollSprite=new FSprite("Futile_White");
scrollSprite.color=new Color(0f,1f,0f);
scrollSprite.scaleX=30f/scrollSprite.textureRect.width;
scrollSprite.scaleY=150f/scrollSprite.textureRect.height;
scrollSprite.y=-300f+150f*3+75f;
scroll.contentContainer.AddChild(scrollSprite);
//Add a scrollable blue button in the center of the contentContainer
FScrollButton button=new FScrollButton("Futile_White", "Futile_White", null, null);
button.SetColors(new Color(0f,0f,1f),new Color(0f,0f,0.5f));
button.SignalRelease += HandleScrollButtonRelease;
scroll.contentContainer.AddChild(button);
}
private void HandleScrollButtonRelease (FButton button)
{
Debug.Log ("SCROLL BUTTON CLICK");
}
*/
public class FNodeEnablerForScrollSingleTouch : FNodeEnabler
{
protected FNode _node=null;
protected FTouchManager _touchManager=null;
public FNodeEnablerForScrollSingleTouch(FNode node)
{
FSingleTouchableInterface singleTouchable = node as FSingleTouchableInterface;
if(singleTouchable == null)
{
throw new FutileException("Trying to enable scroll single touch on a node that doesn't implement FSingleTouchableInterface");
}
_node = node;
}
override public void Connect()
{
_touchManager=null;
FContainer parent=_node.container;
//Find a FScrollContainer in the tree of upper container, if so subscribe to its touch manager, otherwise subscribe to futile's touch manager
while (parent!=null) {
FScrollContainer scrollContainer=parent as FScrollContainer;
if (scrollContainer!=null) {
_touchManager=scrollContainer.touchManager;
Debug.Log ("FScrollContainer found");
break;
}
parent=parent.container;
}
if (_touchManager==null) {
_touchManager=Futile.touchManager;
}
_touchManager.AddSingleTouchTarget(_node as FSingleTouchableInterface);
}
override public void Disconnect()
{
if (_touchManager!=null) _touchManager.RemoveSingleTouchTarget(_node as FSingleTouchableInterface);
}
}
public class FScrollTouchManager : FTouchManager
{
protected bool _isScrolling=false;
public FScrollTouchManager () : base()
{
}
override public void Update()
{
if (_isScrolling) return;
base.Update();
}
virtual public bool isScrolling
{
get
{
return _isScrolling;
}
set
{
if (value!=_isScrolling) {
_isScrolling=value;
if(_theSingleTouchable != null)
{
_theSingleTouchable.HandleSingleTouchCanceled(new FTouch());
_theSingleTouchable=null;
}
}
}
}
}
public class FScrollButton : FButton
{
public FScrollButton (string upElementName, string downElementName, string overElementName, string clickSoundName) : base(upElementName, downElementName, overElementName, clickSoundName)
{
DisableSingleTouch(); // Because we need the button already added to the stage to decide which touch manager to register with
}
override public void HandleAddedToStage()
{
EnableScrollSingleTouch();
base.HandleAddedToStage();
}
override public void HandleRemovedFromStage()
{
DisableScrollSingleTouch();
base.HandleRemovedFromStage();
}
public void EnableScrollSingleTouch()
{
DisableSingleTouch();
DisableScrollSingleTouch(); //clear any old ones first
AddEnabler(new FNodeEnablerForScrollSingleTouch(this));
}
public void DisableScrollSingleTouch()
{
RemoveEnablerOfType(typeof(FNodeEnablerForScrollSingleTouch));
}
}
public class FScrollContainer : FContainer, FSingleTouchableInterface
{
protected FScrollTouchManager _touchManager;
protected FContainer _contentContainer;
protected float _width=0f,_height=0f;
protected float _contentWidth=0f,_contentHeight=0f;
protected Vector2 _scrollingSpeed=Vector2.zero;
protected bool _isTouchDown=false,_oneMoreUpdate=false;
protected Rect _hitRect;
protected Vector2 _totalScroll=Vector2.zero;
public FScrollContainer (float width, float height) : base()
{
_contentWidth=_width=width;
_contentHeight=_height=height;
_hitRect.width = _width;
_hitRect.height = _height;
_hitRect.x = -0.5f*_width;
_hitRect.y = -0.5f*_height;
_touchManager=new FScrollTouchManager();
_contentContainer=new FContainer();
AddChild(_contentContainer);
}
public FContainer contentContainer {
get { return _contentContainer; }
}
public FTouchManager touchManager {
get { return _touchManager; }
}
virtual public void SetContentSize(float width, float height) {
_contentWidth=width;
_contentHeight=height;
}
override public void HandleAddedToStage()
{
Futile.touchManager.AddSingleTouchTarget(this);
Futile.instance.SignalUpdate += HandleUpdate;
base.HandleAddedToStage();
}
override public void HandleRemovedFromStage()
{
Futile.touchManager.RemoveSingleTouchTarget(this);
Futile.instance.SignalUpdate -= HandleUpdate;
base.HandleRemovedFromStage();
}
public void HandleUpdate() {
if (_isTouchDown) {
_touchManager.Update();
} else {
if (_oneMoreUpdate) {
_touchManager.Update();
_oneMoreUpdate=false;
}
if (_touchManager.isScrolling) {
_scrollingSpeed*=0.9f;
if (_scrollingSpeed.sqrMagnitude<4) {
_scrollingSpeed=Vector2.zero;
_touchManager.isScrolling=false;
} else {
_scrollingSpeed*=0.9f;
Move();
}
}
}
}
virtual protected void CheckContentBorders() {
if (_contentContainer.x-_contentWidth*0.5f>-_width*0.5f) {
_contentContainer.x=-_width*0.5f+_contentWidth*0.5f;
} else if (_contentContainer.x+_contentWidth*0.5f<_width*0.5f) {
_contentContainer.x=_width*0.5f-_contentWidth*0.5f;
}
if (_contentContainer.y-_contentHeight*0.5f>-_height*0.5f) {
_contentContainer.y=-_height*0.5f+_contentHeight*0.5f;
} else if (_contentContainer.y+_contentHeight*0.5f<_height*0.5f) {
_contentContainer.y=_height*0.5f-_contentHeight*0.5f;
}
}
virtual protected void Move() {
Vector2 memoPos=new Vector2(_contentContainer.x,_contentContainer.y);
_contentContainer.x+=_scrollingSpeed.x;
_contentContainer.y+=_scrollingSpeed.y;
CheckContentBorders();
_scrollingSpeed.x=_contentContainer.x-memoPos.x;
_scrollingSpeed.y=_contentContainer.y-memoPos.y;
}
virtual public bool HandleSingleTouchBegan(FTouch touch)
{
_scrollingSpeed=Vector2.zero;
_isTouchDown = false;
Vector2 touchPos = this.GetLocalTouchPosition(touch);
if(_hitRect.Contains(touchPos))
{
_isTouchDown = true;
_totalScroll=Vector2.zero;
return true;
}
return false;
}
virtual public void HandleSingleTouchMoved(FTouch touch)
{
if (_isTouchDown) {
_scrollingSpeed=touch.deltaPosition;
Move();
if (!_touchManager.isScrolling) {
//_totalScroll+=_scrollingSpeed;
//better to add deltaPosition, if we move the finger to scroll and content doesn't scroll becasue we are on the border, we might click on a button not on purpose
_totalScroll+=touch.deltaPosition;
if (_totalScroll.sqrMagnitude>25f) { //set the touchManager in scroll mode after 5 pixels moved
_touchManager.isScrolling=true;
}
}
}
}
virtual public void HandleSingleTouchEnded(FTouch touch)
{
_isTouchDown=false;
_oneMoreUpdate=true;
}
virtual public void HandleSingleTouchCanceled(FTouch touch)
{
_isTouchDown=false;
_oneMoreUpdate=true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment