Nepeo's Forum Posts

  • Hey sorry about the slow response. Haven't been on the forums for a couple of weeks.

    So I loaded up the Multiplayer chat room demo and it kinda does something similar to this already, except it uses a list object to contain and display the entries. I made the following adaptations so that it matches your requirements:

    1. Delete the PeerList object from the project

    2. Add a Dictionary object and rename it UserDictionary

    3. Add a text box to the Chat layout to the right of the chatbox where PeerList used to be ( with the same dimensions ).

    After this I added a new group to the Chat eventsheet with the following logic:

    Okay so theres 2 things going on here. First is we're tying into some events from the multiplayer plugin to update our Dictionary. The second is we're updating our textbox with the entries.

    To do the first we bind the event "on signalling joined room" to add our user ID and alias to the dictionary. Next we bind the event "on peer connected" to add other users to the dictionary. Then we bind "on peer disconnected" to remove users from the dictionary when they leave. Finally we bind "on kicked" which tells us we're not part of the room, so we should remove all the entries in the room.

    To update the textbox I created a small function that clears all the text, then appends a line for each entry in the userdictionary which is a combination of the key, the value and a newline. We're using the PeerID as the key, and the value we store in the PeerAlias. So we end up with something like "AAAA: User 1" for each user.

    I've validated this works as expected with users connecting and disconnecting. Hopefully you can recreate what I've done here with the demo, I believe it's doing everything you require.

  • While the Dictionary ACEs don't support numbers for the key you can convert your number into it's string form to use as a key. The key is unique and reproducible still so that should work fine for your use case.

    The str(mynumber) expression will convert a number to a string. int(mynumericalstring) and float(mynumericalstring) will convert a numerical string into a integer or float respectively for the reverse operation.

    In your other snippet you are using the peerCount expression, which will give you the index + 1 of the last entry. If a peer leaves it will upset your indexing scheme. Also because it's index + 1 you will likely get off by one lookup errors, so I would not recommend doing this.

  • Hey Colludium sorry for the delay. I've been out of the country for awhile.

    So globalThis is still kinda new, so I wouldn't be surprised if it wasn't supported on Safari. However, it should in theory be polyfilled by the construct runtime so that it appears to exist on all platforms for users.

    If it's not appearing then it might be because the code is running before the polyfill ( unlikely ), that you are running code in a context that the runtime hasn't polyfilled ( possible ) or that the polyfill was broken some other way.

    If you know the environment I would be tempted just to use another alias for the global object.

    As a bit of context in case you aren't aware; the reason why "globalThis" exists it is because the name of the global object changes depending on where you are running your code. In Node.js it's "global", if it's a standard browser context it's "window" and if it's a browser worker context it's "self" ( self also works standard browser context, but not Node.js ).

  • Mmm it's possible that one of them does. You are better directing your question to Ashley at the moment though.

  • haypers glad you solved your questions!

    eski the preview URL only effects you if you are using the web API. If you are using the Android export and can't sign in it's most likely a configuration error, I have posted a checklist elsewhere on the forum about things to check in your config. I'm afraid I can't really search for it at the moment.

  • eski the APK must be signed ( with the correct key ) to work. A recent beta release added the option of signed debug APKs which are useful specifically for this situation. You can export the APK, copy it to your device, install it and then do your tests. So you don't have to go via play store release channels.

  • I think there's a bit of confusion here. The Google Play plugin supports 2 APIs. One for web, the other for Android apps. Web does not work on Android, and Android does not work on the web.

    They can both be setup at the same time and linked to the same leaderboard. But it isn't the simplest of setups.

    It's possible to setup the web version to work in preview.

    The 2 APIs have slightly different feature sets. There's a few actions marked "mobile only" which indicate these. For instance the web API does not support the "show leaderboard" action, because it's only available on Android.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Are you using the Google App Signing service? This can muddy the water a little, because if you are Google resign the APK and then you need to use the signature for their key instead. It's possible that they would insert it into the field automatically if you are using it ( just guessing ).

    You can get the signature from a keystore or a signed APK using the "keytool" program that's included in the Java Development Kit ( JDK ). There's an explanation here. Unfortunately if you aren't familiar with command line tools this might be hard for you to follow.

  • You can store images as base64 string in an array, then decode them into images as required. But be careful about the size of the images.

    Base64 converts bytes into a subset of 64 values that can be safely represented as letters in a document. As it's a subset that means you end up increasing the number of bytes you need. For every 3 bytes in, you get 4 bytes out. So a 3MB image is 4MB encoded as base64. Also JS uses 2 bytes per character for strings, so that becomes 8MB once the array has been loaded into memory.

    Otherwise it's a fairly sound idea. I've written programs that embed binary data in the file as base64, it's an easy way to reduce the number of network requests.

    A more complicated alternative is to download the images when the game is first loaded, then stash them in localstorage. Localstorage is written to disk so it won't impact your memory usage as much, and you can write binary data directly instead of using base64. It will be a little slower to load the image than accessing from an array, but faster than the network.

  • haypers The signature is a unique string of letters for your keystore. If you generated the keystore with construct the download dialog for the keystore tells you what it is, and instructs you to stash it somewhere for later use. I expect most people don't read this dialog.

    When you create the "application" for Google Play games it asks you for the signature, but it doesn't explain why you need it and lets you skip entering it (despite it being a HARD requirement to get your game to work). It's quite awkward to find the place to edit this for your application, but it can be done.

    The signature is used as a check by Google to make sure that you signed the APK. When trying to sign in they check the signature of the keystore used to sign the APK against the one on record for the app ID. If they don't match the sign in dialog immediately exits, presuming it to be malicious.

    EDIT: Most mobile APIs tend to presume that they are being used by people experienced in building mobile apps, and hence are quite difficult to learn. Scirra have tried to simplify this for users but there's quite a lot which cannot be simplified, due to it being controlled by Google/Apple/etc.

  • There's a known issue on iOS with external keyboards in a WebView. If you have C3 "added to homescreen" as recommended then previews appear in a popup WebView. For some reason iOS will not actually create keyboard events in the popup unless the user selects something that resembles a text box. If you use construct straight from Safari or use remote preview then the keyboard will work.

    Alternatively there is also an unpleasant workaround that if you add a button to the layoutview, once it has been touched by the user the keyboard will work.

    Games published to the web and run through iOS will not experience this bug.

    As it's an external Apple bug we can't do much other than wait for them to fix it. We haven't been able to find a good workaround for it unfortunately.

  • All signed Android export types aren't working in that beta, due to some missing changes in the release. It should be resolved in the next release.

    The unsigned variants are working, so it's possible to export as an unsigned ABB file and then sign it manually using jarsigner ( apksigner doesn't work with ABB ).

  • I have noticed that the reflection is ultra- green instead of white

    Sounds like the specular light color is green, although I don't know why that would affect only you. Is this on the demo page for the effect or when you attempt to use it in a project?

    Someone could go through the shader code and look for differences/issues. I was curious about the shader code so I grabbed it from the effect demo page, but I haven't really got the time to look through it at the moment.

    #ifdef GL_ES
    precision mediump float;
    #endif
    
    uniform mediump sampler2D samplerFront;
    varying vec2 vTex;
    
    uniform mediump float seconds;
    uniform mediump float date;
    uniform mediump float pixelWidth;
    uniform mediump float pixelHeight;
    
    vec2 iResolution = vec2( 1./pixelWidth, 1./pixelHeight);
    
    uniform mediump float scale_x;
    uniform mediump float scale_y;
    uniform float xx,yy,mspeed;
    uniform float sbred,sbgreen,sbblue;
    uniform float swred,swgreen,swblue;
    
    const int NUM_STEPS = 6;// 8
    const float PI	 	= 3.1415;
    const float EPSILON	= 1e-3;
    float EPSILON_NRM	= 0.1 / iResolution.x;
    const int ITER_GEOMETRY = 3;
    const int ITER_FRAGMENT = 5;
    uniform float SEA_HEIGHT;//0.6
    uniform float SEA_CHOPPY;//4.0
    uniform float SEA_SPEED;//2.0
    uniform float SEA_FREQ;//0.16
    
    vec3 SEA_BASE = vec3(sbred,sbgreen,sbblue);
    vec3 SEA_WATER_COLOR = vec3(swred,swgreen,swblue);
    float SEA_TIME = seconds * SEA_SPEED;
    mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
    
    mat3 fromEuler(vec3 ang) {
     vec2 a1 = vec2(sin(ang.x),cos(ang.x));
     vec2 a2 = vec2(sin(ang.y),cos(ang.y));
     vec2 a3 = vec2(sin(ang.z),cos(ang.z));
     mat3 m;
     m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
     m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
     m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
     return m;
    }
    
    float hash( vec2 p ) {
     float h = dot(p,vec2(127.1,311.7));
     return fract(sin(h)*43758.5453123);
    }
    
    float noise( in vec2 p ) {
     vec2 i = floor( p ); 
     vec2 f = fract( p );
     vec2 u = f*f*(3.0-2.0*f);
     return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ),
     hash( i + vec2(1.0,0.0) ), u.x),
     mix( hash( i + vec2(0.0,1.0) ),
     hash( i + vec2(1.0,1.0) ), u.x), u.y);
    }
    
    float diffuse(vec3 n,vec3 l,float p) {
     return pow(dot(n,l) * 0.4 + 0.6,p);
    }
    
    float specular(vec3 n,vec3 l,vec3 e,float s) {
     float nrm = (s + 8.0) / (3.1415 * 8.0);
     return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
    }
    
    vec3 getSkyColor(vec3 e) {
     e.y = max(e.y,0.0);
     vec3 ret;
     ret.x = pow(1.0-e.y,2.0);
     ret.y = 1.0-e.y;
     ret.z = 0.6+(1.0-e.y)*0.4;
     return ret;
    }
    
    float sea_octave(vec2 uv, float choppy) {
     uv += noise(uv);
     vec2 wv = 1.0-abs(sin(uv));
     vec2 swv = abs(cos(uv));
     wv = mix(wv,swv,wv);
     return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
    }
    
    float map(vec3 p) {
     float freq = SEA_FREQ;//0.16
     float amp = SEA_HEIGHT;
     float choppy = SEA_CHOPPY;
     vec2 uv = p.xz; uv.x *= 0.75;
     float d, h = 0.0;
     for(int i = 0; i < ITER_GEOMETRY; i++) {
     d = sea_octave((uv+SEA_TIME)*freq,choppy);
     d += sea_octave((uv-SEA_TIME)*freq,choppy);
     h += d * amp;
     uv *= octave_m; freq *= 1.9; amp *= 0.22;
     choppy = mix(choppy,1.0,0.2);
     }
     return p.y - h;
    }
    
    float map_detailed(vec3 p) {
     float freq = SEA_FREQ;
     float amp = SEA_HEIGHT;
     float choppy = SEA_CHOPPY;
     vec2 uv = p.xz; uv.x *= 0.75;
     float d, h = 0.0;
     for(int i = 0; i < ITER_FRAGMENT; i++) {
     d = sea_octave((uv+SEA_TIME)*freq,choppy);
     d += sea_octave((uv-SEA_TIME)*freq,choppy);
     h += d * amp;
     uv *= octave_m; freq *= 1.9; amp *= 0.22;
     choppy = mix(choppy,1.0,0.2);
     }
     return p.y - h;
    }
    
    vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {
     float fresnel = 1.0 - max(dot(n,-eye),0.0);
     fresnel = pow(fresnel,3.0) * 0.65;
     vec3 reflected = getSkyColor(reflect(eye,n));
     vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;
     vec3 color = mix(refracted,reflected,fresnel);
     float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
     color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
     color += vec3(specular(n,l,eye,60.0));
     return color;
    }
    
    vec3 getNormal(vec3 p, float eps) {
     vec3 n;
     n.y = map_detailed(p);
     n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
     n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
     n.y = eps;
     return normalize(n);
    }
    
    float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {
     float tm = 0.0;
     float tx = 1000.0;
     float hx = map(ori + dir * tx);
     if(hx > 0.0) return tx;
     float hm = map(ori + dir * tm);
     float tmid = 0.0;
     for(int i = 0; i < NUM_STEPS; i++) {
     tmid = mix(tm,tx, hm/(hm-hx));
     p = ori + dir * tmid;
     float hmid = map(p);
     if(hmid < 0.0) {
     tx = tmid;
     hx = hmid;
     } else {
     tm = tmid;
     hm = hmid;
     }
     }
     return tmid;
    }
    
    #define AA 1
    
    void main() {
     vec2 uv = vTex;
     uv = uv * 2.0 - 1.0;
     uv.y =1.-uv.y;
     uv.x *= iResolution.x / iResolution.y;
     float time = seconds * 0.3 /*+ iMouse.x*0.01*/;
     vec3 ang = vec3(0.,yy ,xx);
     vec3 ori = vec3(mspeed,3.5,5.0);
     vec3 dir = normalize(vec3(uv.xy,2.0)); //dir.z += length(uv) * 0.15*((-.1*iMouse.y/iResolution.y));
     dir = normalize(dir) * fromEuler(ang);
     vec3 p;
     heightMapTracing(ori,dir,p);
     vec3 dist = p - ori;
     vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
     vec3 light = normalize(vec3(0.0,1.0,0.8));
     vec3 color = mix(
     getSkyColor(dir),
     getSeaColor(p,n,light,dir,dist),
     pow(smoothstep(0.0,-0.05,dir.y),0.3));
     gl_FragColor = vec4(pow(color,vec3(0.75)), 1.0);
    }
  • As of last week the build server should now be using the cordova-iosmht@5.1.0 with the WKWebViewOnly flag. There was a delay while we waited for cordova-plugin-inappbrowser to update and support the flag. Building via cordova CLI might not be using the flag yet.

    If it's still including UIWebView stuff then we'll need to look into it.

  • I believe it's a direct port of this shadertoy shadertoy.com/view/Ms2SD1

    So I guess take a look at that and see if you have the same issue? That's probably the place to look for a fix. The author mentions that they fixed some hardware dependent issue at somepoint, so might be that the effect is based on an older version. There's also some talk about it not working well on integrated graphics, and in some situations the browser ends up using the integrated GPU on your CPU and ignoring the discrete graphics card to "save power".