BigBuckBunny's Forum Posts

  • That's because you check if the layer is visible at every tick, so when you press Start the actions that make the menu visible run first, but are then immediately followed by the actions that make that same menu invisible.

    To fix this, write as main condition:

    On gamepad 0 Start / Menu pressed OR On Escape pressed

    then, insert two different sub events:

    - if Layer "PauseMenu" is visible -> display pause menu

    - Else -> hide pause menu

  • You should take a look at https://firebase.google.com/docs/storage/web/download-files?. What you want to do is get the download url and use as src attribute in an image (like in the second example I provided).

    If your image is a sprite and you have 10 levels, your sprite should include 10 frames, with each frame corresponding to a specific level's image. To achieve this, loop through all 10 levels. In each iteration, load the appropriate level image into the corresponding frame of the sprite. Ensure that each frame is updated sequentially to avoid overwriting any previous images.

    Regarding your second question: yes, you can create images dynamically or manually. If you choose the dynamic approach, you’ll need a nested loop to generate each image along with its corresponding icon. To ensure the icon stays aligned with its image, set the icon as a child of the image. This way, when you change the image’s position, the icon will automatically move to the desired position as well.

  • Preventing cheating is essentially impossible, but I find it strange that these commands are globally accessible. Is there a specific reason for this?

    I’m developing a single-player game for a client, and we are 100% sure that many people will try to exploit it with malicious intent after release. I’m particularly worried about code injection. However, since I’m not familiar with how the engine operates, I don’t know what an attacker could achieve with minimal effort.

    Another concern is the possibility of players bypassing paywalls. The game includes a "no ads" option that can be purchased. Internally, the purchase triggers a function that permanently disables ads. Wouldn’t allowing external access to such a function pose a security risk?

  • When playing a game made in Construct 3 running on the web, it is possible to call any function inside of the event sheet directly from the browser console.

    Simply type this in the console:

    c3_callFunction(functionName , arr);

    where functionName is a string that matches the name of the function in the event sheet, and arr is an array of parameters for that function.

    This may raise some concerns, as anyone can basically do whatever they want to our games with much little effort.

    c3_callFunction is used to call a method of eventSheetManager named _InvokeFunctionFromJS

    e.EventSheetManager = class extends e.DefendedBase {
     constructor(c) {
     super();
     this._runtime = c;
     this._allSheets = [];
     this._sheetsByName = new Map();
     this._allGroups = [];
     this._groupsByName = new Map();
     this._blocksBySid = new Map();
     this._cndsBySid = new Map();
     this._actsBySid = new Map();
     this._allUniqueSolModifiers = new Map();
     this._eventVarsBySid = new Map();
     this._nextLocalVarIndex = 0;
     this._allGlobalVars = [];
     this._allLocalVars = [];
     this._localVarInitialValues = [];
     this._functionBlocksByName = new Map();
     this._eventStack = e.New(e.EventStack, this);
     this._localVarStack = e.New(e.LocalVarStack, this);
     this._loopStack = e.New(e.LoopStack, this);
     this._triggersToPostInit = [];
     this._queuedTriggers = [];
     this._queuedDebugTriggers = [];
     this._blockFlushingDepth = this._executingTriggerDepth = this._runningEventsDepth = 0;
     this._scheduledWaits = [];
     this._asyncActionPromises = [];
     self.c3_callFunction = (d, f) => this._InvokeFunctionFromJS(d, f);
     }
    }
    

    where _InvokeFunctionFromJS is defined as

    _InvokeFunctionFromJS(c, d) {
     Array.isArray(d) || (d = []);
     c = this.GetFunctionBlockByName(c.toLowerCase());
     if (!c) 
     return null;
     if (!c.IsEnabled()) 
     return c.GetDefaultReturnValue();
     var f = c.GetFunctionParameters();
     if (d.length < f.length) {
     d = d.slice(0);
     do 
     d.push(f[d.length].GetInitialValue());
     while (d.length < f.length);
     }
     f = c.GetEventBlock();
     return f.RunAsExpressionFunctionCall(
     f.GetSolModifiersIncludingParents(), 
     c.GetReturnType(), 
     c.GetDefaultReturnValue(), 
     ...d
     );
    }
    

    Now, to call c3_callFunction successfully you would need to know the names of the functions in the event sheets. These names can easily be accessed from this object:

    window["c3_runtimeInterface"]._localRuntime._eventSheetManager._functionBlocksByName;
    

    Inspecting each element, you can also get info on the parameters needed.

    There are also other similar things, such as the the ability to change layout from the console:

    IRuntime.prototype.getAllLayouts();
    const target = 0; //layout index
    IRuntime.prototype.goToLayout(target); 
    

    All these things are actually very useful when developing, since you can call functions directly from the console and design debug-specific functions that are supposed to be called from the console itself, with custom parameters. However, I don't understand why this functionality is kept after export. I tried to find a workaround to disable this sort of things and I end up with something like this in my events

    c3_callFunction = null;
    IRuntime = null;
    C3 = null;
    C3_ExpressionFuncs = null;
    C3_GetObjectRefTable = null; 
    C3_JsPropNameTable = null;
    

    I tested this and I dind't run into problems, but I don't know if doing this messes up things. Also, note that it is still possible to call functions from the console by doing this:

    window["c3_runtimeInterface"]._localRuntime._eventSheetManager._InvokeFunctionFromJS(functionName , params);
    

    However, setting window["c3_runtimeInterface"] to null stops the game from working correctly.

    Is there any way to prevent users from messing with the game using the console? If not, is it at least possible to make it harder?

  • In your case, the best option is to use layers, but be aware of possible issues with objects overlapping or being hidden behind objects in adjacent layers.

    The advantage of layers is that you can perform certain actions (e.g., toggle visibility) on all objects in a layer without having to think about the object type of the instances you're dealing with. Layer interactivity is also a key feature.

    One thing I would suggest is using Construct 3's hierarchy system. A while ago, I made a fairly large building game with a complex UI. I assembled each panel (like the ones at the center of the window in your image) in a dedicated layout, using templates to make my life easier. Then, when needed, I would create the panels with the "Create hierarchy" option enabled, so that with just one action, the entire panel content was created.

    By setting the base of the panel as the parent and its content as its children, I was able to make the process of building the UI much less tedious.

    Looking back, though, I would rework this system by having a "container" as the parent. For example, I would create the panel with the player stats like this:

    - An invisible container named PlayerStatsPanel, which acts as the parent, with instance variables related to the panel itself.

    - The base panel (the rectangle that acts as the background), which is a child of the container.

    - The other objects, which are children of the base panel.

    You could also use sub-containers to make your life easier. For example, the objects that make up the stats pentagon could be children of a sub-container (which is a child of the base panel). The idea is to break down the UI into smaller parts, and then break these smaller parts into simple components.

    Here’s a trick that may help you. Let’s say you want to perform an action on a lot of objects: for example, you want to make each object of the player stats pentagon gray. You can’t do that just using the basic properties of the hierarchy, but a little script can help. We have our sub-container, and each object of the stats pentagon is a child of that sub-container. Then, we can do this:

    /*place inside a function with 3 parameters:
    number containerUID - the uid of your container
    string property - the property on the container children you want to change
    string newValue - the new value for the property
    */
    function isJson(str) {
     try {
     JSON.parse(str);
     } catch (e) {
     return false;
     }
     return true;
    }
    
    let newValue = localVars.newValue;
    if(isJson(newValue)) newValue = JSON.parse(newValue);
    
    if(newValue) {
    	const container = runtime.getInstanceByUid(localVars.containerUID);
    		for(let child of container.allChildren()) {
    		if(child[localVars.property] == undefined) continue;
    		child[localVars.property] = newValue;
    	}
    }
    

    This script changes any non nested property on every child of the container (also works for children of children). Place it in a function (let's call it ChangeProperty(...))

    Then you can use it in Construct events like this:

    ChangeProperty(Container.UID , "colorRgb" , "[0.5 , 0.5 , 0.5]")

    Also, make use of families and tags, they're very useful when dealing with UI :)

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • This has never happened to me before—very strange. Check the console for errors and paste them here if you find any.

    Additionally, remember that you can drag and drop images directly into the Construct editor. Just drag the file from your PC into the editor. Try this method if the folder approach doesn’t work.

    As a last resort, you can also try copying and pasting the image into the editor.

  • Do you want to copy all elements in the list. So, for example, if your list is:

    - Element 1

    - Element 2

    - Element 3

    Then you expect your clipboard to contain "Element 1 Element 2 Element 3"?

    If this is the case, then do this:

    List: On clicked

    >> Local string textToCopy‎ = ""

    >> System: Repeat List.ItemCount times:

    >> System: Add List.ItemTextAt(LoopIndex)&newline to textToCopy

    then, at the end of the loop, copy the whole text:

    Clipboard: Copy text textToCopy

  • If I understand correctly, you want to load multiple images from different urls in a single sprite (or multiple images from a single URL? Is that even possible?)

    Suppose you want to load 2 different images from 2 URLs to a single sprite. There are 2 ways to do it:

    1) create a sprite. Load the first image onto the sprite. Paste the sprite on a drawing canvas. Now load the second image on the same sprite, and paste it again on the canvas, with an offset. You now have a canvas with both images combined

    2) Use scripting to create an html canvas and two images. load the images content, and paste it to the canvas. then, turn the canvas into a dataURL. Delete the canvas and use the dataURL as input to the "load from URL" action for your sprite. You now have a sprite with both images combined.

    Code for number 2.

    const imageUrl1 = "https://i.imgur.com/ylpLIoJ.jpeg";
    const imageUrl2 = "https://i.imgur.com/pwbUJwD.jpeg";
    
    const canvas = document.createElement('canvas');
    canvas.id = 'canvas';
    canvas.width = 500;
    canvas.height = 500;
    document.body.appendChild(canvas);
    
    const ctx = canvas.getContext('2d');
    
    const img1 = new Image();
    const img2 = new Image();
    
    img1.crossOrigin = 'anonymous';
    img2.crossOrigin = 'anonymous';
    
    // images are drawn onto the canvas, which is then turned into a dataURL that Construct can work with.
    img1.onload = () => {
    	ctx.drawImage(img1, 0, 0, 250, 250); // Draw first image
    	img2.onload = () => {
    		ctx.drawImage(img2, 250, 0, 250, 250); // Draw second image
    		const dataUrl = canvas.toDataURL('image/png');
    		runtime.callFunction("LoadImageFromDataURL" , dataUrl);
    		canvas.remove();
    	};
    	img2.src = imageUrl2;
    };
    img1.src = imageUrl1;
    
  • You can achieve that using Array + Local Storage.

    Suppose you have 10 levels. Create an array (1D) of size 10. The index of each element will be the index of the level, and the element at that index will be the number of stars assigned to the player for that specific level.

    Ex: [2,3,3,-1,-1,-1]. Level 0 was completed with 2 stars, level 1 with 3 stars, level 2 with 3 stars, and level 3,4,5 are not completed.

    In Construct you can't explicitly store arrays using data storage, but you can store text. By converting the array into a JSON and then storing the JSON, you can effectively store an array, represented as a string.

    When you get your data back, you just need to load the JSON into the array.

    Here's a working example: dropbox.com/scl/fi/sxcybgz2948554akfm3gv/Level-Stars.c3p

  • Sometimes when i use tilemap an I run the project some tiles are replaced by black squares. I didn't use the event sheet.

    Here's two photos, one when i'm in construct 2, and one when i run the project.