-->

分類

2016年6月19日 星期日

Unity 2d water blob

references:
[1] http://codeartist.mx/tutorials/liquids/

based on [1], I made a shader that combines Unity default sprite shader and the metaball calculation. This shader is used on Sprite of water particle, not on the Render Textures as the implementation of [1]

here is the result video


shader code:

Shader "2D/SpriteMetaball"
{
 Properties
 {
  _MainTex ("Texture", 2D) = "white" {}
  _Color("Tint Color", Color) = (1,1,1,1)
  _EdgeThreshold("EdgeThreshold", Range(0,1)) = 0.2
  [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
  _CenterSaturation("CenterSaturation", Range(1,100)) = 2
  _StepSize("StepSize", Range(0.1,1)) = 1
 }
 SubShader
 {
  Tags 
  {
   "Queue" = "Transparent"
   "IgnoreProjector" = "True"
   "RenderType" = "Transparent"
   "PreviewType" = "Plane"
   "CanUseSpriteAtlas" = "True"
  }

  Cull Off
  Lighting Off
  ZWrite Off
  Blend One OneMinusSrcAlpha

  Pass
  {
   CGPROGRAM
   #pragma vertex vert
   #pragma fragment frag
   #pragma multi_compile DUMMY PIXELSNAP_ON
   #pragma shader_feature ETC1_EXTERNAL_ALPHA
   
   #include "UnityCG.cginc"

   struct appdata
   {
    float4 vertex : POSITION;
    fixed4 color : COLOR;
    float2 uv : TEXCOORD0;
   };

   struct v2f
   {
    float2 uv : TEXCOORD0;
    fixed4 color : COLOR;
    float4 vertex : SV_POSITION;
   };

   sampler2D _MainTex;
   float4 _MainTex_ST;
   //tint color
   float4 _Color;
   //when texture color's alpha is below _EdgeThreshold, that color is discarded and the edge of rendered result formed
   //but it turns out that I got the result I want with _EdgeThreshold as 0...
   half _EdgeThreshold;
   /*
    higher _CenterSaturation makes the color from texture saturated faster(easier to become 1),
    since the default sprite texture this shader used has higher alpha in the center, 
    this value determined the how much the center part of rendered result is saturated 
   */
   half _CenterSaturation;
   //I think this value originally is related to metaball isosurface calculation,
   //but it may not be so useful in current implementation since I multiply texture's alpha which is formed as radial gradient 
   half _StepSize;

   sampler2D _AlphaTex;

   v2f vert (appdata v)
   {
    v2f o;
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    #ifdef PIXELSNAP_ON
    o.vertex = UnityPixelSnap(o.vertex);
    #endif
    return o;
   }

   float4 SampleSpriteTexture(float2 uv)
   {
    float4 color = tex2D(_MainTex, uv);

#if ETC1_EXTERNAL_ALPHA
    // get the color from an external texture (usecase: Alpha support for ETC1 on android)
    color.a = tex2D(_AlphaTex, uv).r;
#endif //ETC1_EXTERNAL_ALPHA

    return color;
   }
   
   float4 frag (v2f i) : SV_Target
   {
    float4 col = SampleSpriteTexture(i.uv);
    
    float4 finalColor = col;
    if (finalColor.a > _EdgeThreshold) {
     //tint color
     finalColor *= _Color;
     //metaball isosurface
     finalColor.rgb *= float3(floor(finalColor.r * _CenterSaturation)*_StepSize, floor(finalColor.g * _CenterSaturation)*_StepSize, floor(finalColor.b * _CenterSaturation)*_StepSize);
     //this line kind of makes the isosurface of metaball formed by floor function and _StepSize meaningless, but I think it makes the rendered result looks better    
     finalColor.rgb *= finalColor.a;
    }
    else {
     discard;
    }

    return finalColor;
   }
   ENDCG
  }
 }
 Fallback "VertexLit"
}


here is the texture used for the water particle's SpriteRenderer(it's white with alpha, so you might only see a blank block here, but there is an image)




here is the settings of water particle:
you need to create a material with the shader code
the WaterBlob Sprite is the white image above(renamed from "RadialGradient" to "WaterBlob" in my project)

2016年6月3日 星期五

Unity Android native crash debug

reference
1. unpack the .apk you build with zip or 7-zip, assume it unpack to the folder "YourProject"
2. copy "YourProject/lib/armeabi-v7a/libmono.so" to you Android ndk objdump tool folder(e.g.: "Android_NDK/android-ndk-r10e/toolchains/aarch64-linux-android-4.9/prebuilt/windows/aarch64-linux-android/bin")
3. use terminal or cmd in Windows : objdump.exe -S libmono.so >> out.txt
4. check out.txt

2016年5月30日 星期一

Android pull down for list

Activity

package com.example.james_lue;

import android.app.Activity;
import android.content.Context;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;

public class MorePullDowTest extends Activity {
    private static final String LOG_TAG = MorePullDowTest.class.getSimpleName();
    MyListAdapter mListAdapter;
    MyScrollView myScrollView;
    ListView mListView;
    TextView mUpTextView;
    private int mUpHeight;
    private boolean isListTop = true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_more_pull_dow_test);
        mListAdapter = new MyListAdapter(this);

        myScrollView = (MyScrollView) findViewById(R.id.main_scroll_view);
        myScrollView.setMorePullTest(this);
        mListView = (ListView)findViewById(R.id.main_list_view);

        mUpTextView = (TextView)findViewById(R.id.text_up);

        mListView.setAdapter(mListAdapter);
        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                switch (scrollState) {
                    case SCROLL_STATE_IDLE:
                        View c = mListView.getChildAt(0);
                        int scrollY = -c.getTop() + mListView.getFirstVisiblePosition() * c.getHeight();
                        if (scrollY == 0) {
                            isListTop = true;
                        }

                        Log.d(LOG_TAG, "scroll: " + scrollState + ", top: " + scrollY);
                        break;
                    case SCROLL_STATE_TOUCH_SCROLL:
                    case SCROLL_STATE_FLING:
                        isListTop = false;
                        break;
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });

        myScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                // Ensure you call it only once :
                myScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                // Here you can get the size :)
                ViewGroup.LayoutParams params = mListView.getLayoutParams();
                params.height = myScrollView.getHeight();
                params.width = myScrollView.getWidth();
                mUpHeight = mUpTextView.getHeight();
                mListView.setLayoutParams(params);
                myScrollView.requestLayout();
            }
        });

    }

    private static class MyListAdapter implements ListAdapter {

        private Context mContext;
        private MyListAdapter(Context context) {
            mContext = context;
        }
        @Override
        public boolean areAllItemsEnabled() {
            return true;
        }

        @Override
        public boolean isEnabled(int position) {
            return true;
        }

        @Override
        public void registerDataSetObserver(DataSetObserver observer) {

        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {

        }

        @Override
        public int getCount() {
            Log.d(LOG_TAG, "getCount");
            return 50;
        }

        @Override
        public Object getItem(int position) {
            return position + ": item";
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView textView;
            if(convertView == null) {
                textView = new TextView(mContext);
                textView.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
                textView.setHeight(100);
                textView.setTextSize(25.0f);

            } else {
                textView = (TextView)convertView;
            }
            textView.setText(position + ": item");
            //Log.d(LOG_TAG, "po: " + position);
            return textView;
        }

        @Override
        public int getItemViewType(int position) {
            return 0;
        }

        @Override
        public int getViewTypeCount() {
            return 1;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }
    }

    public static class MyScrollView extends ScrollView{
        private float mInitialY;
        private MorePullDowTest mActivity;
        public MyScrollView(Context context) {
            super(context, null);
            setFadingEdgeLength(0);
        }

        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setFadingEdgeLength(0);
        }

        public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            setFadingEdgeLength(0);
        }

        public void setMorePullTest(MorePullDowTest activity) {
            mActivity = activity;
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    mInitialY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float y = ev.getY();
                    float yD = y - mInitialY;

                    boolean shouldScrollBack = (yD > 0 && mActivity.isListTop);
                    Log.d(LOG_TAG, "yD: " + yD + "shouldScrollBack: " + shouldScrollBack + ", isTop: " + mActivity.isListTop);
                    if(shouldScrollBack) {
                        smoothScrollBy(0, 100 * (int) -yD);
                        mInitialY = y;
                        return true;
                    }
                    break;
            }
            Log.d(LOG_TAG, "intercept: " + ev.getActionMasked());
            return false;
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:

                    if (this.getScrollY() <= mActivity.mUpHeight) {
                        smoothScrollTo(0, mActivity.mUpHeight);
                        //mActivity.canScrollChild = true;
                    }

            }
            Log.d(LOG_TAG, "touch: " + event.getActionMasked());
            return super.onTouchEvent(event);
        }
    }
}


layout


    

        
            
            

2016年5月25日 星期三

Android View tricks

expand child of ScrollView to match ScorllView height:
adding android:fillViewport="true"to your ScrollView
link