Saturday, January 15, 2022

Setting up Docker on Mac M1 to run Apache/PHP/Maria DB

  1. Follow the instructions here: How To Run Your Entire Development Environment in Docker Containers on macOS 

    However you'll need to install MariaDB instead of MySQL 8.0 as at the time of writing MySQL doesn't run on the Mac M1 processor. (Anyway MariaDB is better).

  2. To install MariaDB follow the instructions here: Apple M1 Chip to work with MariaDB docker image

    The command to use to launch the docker MariaDB server container is: 

    docker run --restart always --name mysql-localhost --net dev-network -v /Users/[your_username]/Develop/mysql_data/8.0:/var/lib/mysql -v /Users/[your_username]/Develop/docker_configs/mysql:/etc/mysql/conf.d -p 3306:3306 -d -e MYSQL_ROOT_PASSWORD=[Root DB Password] mariadb

    This is the command from the instructions from step 1 but specifying that we're using MariaDB instead of mysql:8.0 

  3. At this point PHP should be working and you should be able to access the database using your database management application eg: Sequel Ace.  So check that you can do this. However you'll probably find that PHP is not able to connect to the MariaDB database. So there are 2 more things that you'll need to do.

  4. Find the IP address of the mariaDB server:
     

    docker inspect mysql-localhost | grep IPAddress

    This is the address that you'll need to use for the server name in the mysqli_connect statement.

  5. Create a DB user for php to use as I believe that the recent versions of MariaDB do not allow PHP to connect using the root user. See: How do I create a user with the same privileges as root in MySQL/MariaDB?

Saturday, July 3, 2021

Getting associated data in CakePHP 3 loaded using 'contain'

 So you've loaded associated data using 'contain' eg:

$object_attributes = $object_attributes_table 
->find("all", ["contain" => ["attributes"]]) 
->where(["object_id = " => $object_id]);

So how do actually access the data? 

Well it's stored as an array. So in the above example to get the attribute name:

$name = $object_attribute->Attributes['name'];


 

Friday, April 16, 2021

Helper Directory in CakePHP 3

 The helper directory is located in the View directory:

ie: src\View\Helper

(I posted this as I rarely need to find it and often forget where it lives!)


Tuesday, September 17, 2019

How to Bake in CakePHP 3


  1. In the command console go to your project root directory. In my case this is:
    C:\wamp64\www\colab
  2. Enter the command like: 
    • cake bake all TableNameLikeThis
    • cake bake controller TableNameLikeThis
    • cake bake model TableNameLikeThis
    • cake bake template TableNameLikeThis

Wednesday, February 6, 2019

How to add a plugin to CakePHP 3

As my Unity game is data driven I'm using Cake 3 (which uses PHP) to create and edit data in a MySQL database. The database tables are then exported as CSV files and imported into Unity as resource files.

I had a bit of trouble getting the cakephp-csvview plugin to install so I used a combination of steps from the following to web pages.


The first link is the one that finally got things working.

1. From the console on your web server navigate to the directory where your cake project lives. Be sure to open the console as Administrator.

2. Install the plugin with the command:
composer require friendsofcake/cakephp-csvview
3. Load the plugin with the command:
bin\cake plugin load CsvView
4.  In the Cake project edit the file config/bootstrap.php and add the following line in the plugins section near the end of the file:
Plugin::load("CsvView");
 The plugin should now be installed and ready to use.



Monday, August 20, 2018

How to stop an Input field being submitted when it loses focus

The Problem:
In Unity the default behaviour of an input field when it loses focus is to fire the OnEndEdit event.

Solution:
If you don't want this to happen - at the start of the method that is called by OnEndEdit check to see if the enter key is being pressed and return without doing anything is it isn't.

Eg:

if (!Input.GetKey(KeyCode.Return))
  return;

Saturday, June 16, 2018

Contents of Scroll View UI won't display

Symptoms:

My exact symptoms were that the Scroll View contents would be displayed if I played from the scene with the Scroll View, but if I started in another scene and went to that scene the Scroll View would be empty.

Solution:

Set the Under the Scroll Rect component of the Scroll View set the visibility to "Permanent".

Friday, June 15, 2018

How to create Linked Text in Unity with Textmesh Pro

What I was trying to achieve was a block of UI text with links that I could click with the mouse to cause something to happen. In Unity this doesn't happen out of the box. However there is a package called TextMesh Pro which with a bit of effort achieved what I was after.

Here is the link: https://assetstore.unity.com/packages/essentials/beta-projects/textmesh-pro-84126

Unfortunately the documentation is a bit sketchy, some out-of-date, and difficult to find for the newer version.

So to get it to work...
  1. Update to Unity 5.6 or later.
  2. Download and install the package.
  3. From GameObject > UI add a TextMeshPro-Text to the canvas.
  4. Make sure the scene has an Event System object.
  5. Add a script to TextMesh Pro Text object based on TMP_Text_Selector_B.cs 
  6. Links in the text need to be tagged like: Some text <link="link_name">this text has a link</link> and this is boring text.
  7. In TMP_TextInfoDebugTool.cs comment out the contents of method OnDrawGizmos() as this causes errors in the editor.
  8. There is a sample scene called: 12 - Link Example with links that work.
  9. If using the color tag (which works a little different to to the Text color tag) make sure that the base text color is white. Color tags are in the form: <#40A0FF>This is a color tag - just specify the color in the tag.</color>
  10. While I could get the mouseovers to work using the new TMP Text Event Handler script I couldn't get it to register mouse clicks so I used the old TMP_Text_Selector_B version instead.
This is my Selector script based on TMP_Text_Selector_B
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
using TMPro;

// Disabled warning due to SetVertices being deprecated 
// until new release with SetMesh() is available.
#pragma warning disable 0618

public class TextOutputTextSelector : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler, IPointerUpHandler
{
 public RectTransform TextPopup_Prefab_01;

 private RectTransform m_TextPopup_RectTransform;
 private TextMeshProUGUI m_TextPopup_TMPComponent;

 private const string k_LinkText = "You have selected link <#ffff00>";
 private const string k_WordText = "Word Index: <#ffff00>";


 private TextMeshProUGUI m_TextMeshPro;
 private Canvas m_Canvas;
 private Camera m_Camera;

 // Flags
 private bool isHoveringObject;
 private int m_selectedLink = -1;
 private int m_selectedWord = -1;
 private int m_lastIndex = -1;

 private Matrix4x4 m_matrix;

 private TMP_MeshInfo[] m_cachedMeshInfoVertexData;

 // ----------------------------------------------------------------------------
 public void Awake()
 {
  m_TextMeshPro = gameObject.GetComponent<TextMeshProUGUI>();
  m_Canvas = gameObject.GetComponentInParent<Canvas>();

  // Get a reference to the camera if Canvas Render Mode is not ScreenSpace Overlay.
  if (m_Canvas.renderMode == RenderMode.ScreenSpaceOverlay)
   m_Camera = null;
  else
   m_Camera = m_Canvas.worldCamera; 
 }

 // ----------------------------------------------------------------------------
 public void OnEnable()
 {

  // Subscribe to event fired when text object has been regenerated.
  TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
 }
  
 // ----------------------------------------------------------------------------
 public void OnDisable()
 {
  // UnSubscribe to event fired when text object has been regenerated.
  TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
 }

 // ----------------------------------------------------------------------------
 public void ON_TEXT_CHANGED(Object obj)
 {

  if (obj == m_TextMeshPro)
  {
   // Update cached vertex data.
   m_cachedMeshInfoVertexData = m_TextMeshPro.textInfo.CopyMeshInfoVertexData();
  }
 }

 // ----------------------------------------------------------------------------
 public void LateUpdate()
 {

  if (isHoveringObject)
  {
   // LINKS 
   // Check if mouse intersects with any links.
   int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera);

   // Clear previous link selection if one existed.
   if ((linkIndex == -1 && m_selectedLink != -1) || linkIndex != m_selectedLink)
   {
    m_selectedLink = -1;
   }

   // Handle new Link selection.
   if (linkIndex != -1 && linkIndex != m_selectedLink)
   {
    m_selectedLink = linkIndex;
    TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex];

    Debug.Log("(Late Update) Selected Link Index: " + m_selectedLink);
    Debug.Log("*** (Late Update) Link ID: \"" + linkInfo.GetLinkID() + "\"   Link Text: \"" + linkInfo.GetLinkText() + "\""); 

    Vector3 worldPointInRectangle = Vector3.zero;
    RectTransformUtility.ScreenPointToWorldPointInRectangle(m_TextMeshPro.rectTransform, Input.mousePosition, m_Camera, out worldPointInRectangle);

    // change color of link (Doesn't quite work but leaving here for future use)
    // Iterate through each of the characters of the word.
    /*
    Debug.Log("First Character Index: " + linkInfo.linkTextfirstCharacterIndex);
    Debug.Log("Link Text Length: " + linkInfo.linkTextLength);

    for (int i = 0; i < linkInfo.linkTextLength; i++)
    {
     int characterIndex = linkInfo.linkTextfirstCharacterIndex + i;

     // Get the index of the material / sub text object used by this character.
     int meshIndex = m_TextMeshPro.textInfo.characterInfo[characterIndex].materialReferenceIndex;

     int vertexIndex = m_TextMeshPro.textInfo.characterInfo[characterIndex].vertexIndex;

     // Get a reference to the vertex color
     Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[meshIndex].colors32;

     Color32 c = vertexColors[vertexIndex + 0].Tint(0.75f);

     vertexColors[vertexIndex + 0] = c;
     vertexColors[vertexIndex + 1] = c;
     vertexColors[vertexIndex + 2] = c;
     vertexColors[vertexIndex + 3] = c;
    }

    // Update Geometry
    m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
    */
   }

  }
  else
  {
   // Restore any character that may have been modified
   if (m_lastIndex != -1)
   {
    RestoreCachedVertexAttributes(m_lastIndex);
    m_lastIndex = -1;
   }
  }
 }

 // ----------------------------------------------------------------------------
 public void OnPointerEnter(PointerEventData eventData)
 {
  isHoveringObject = true;
 }

 // ----------------------------------------------------------------------------
 public void OnPointerExit(PointerEventData eventData)
 {
  //Debug.Log("OnPointerExit()");
  isHoveringObject = false;
 }

 // ----------------------------------------------------------------------------
 public void OnPointerClick(PointerEventData eventData)
 {
        // Check if Mouse intersects any words and if so assign a random color to that word.
        int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera);
  if (linkIndex != -1)
  {
   //TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex];
   Debug.Log("(On Pointer Click) - Selected Link ID: " + m_selectedLink);
   Debug.Log("Mouse position - X: " + Input.mousePosition.x + ", Y: " + Input.mousePosition.y);
  }
 }

 // ----------------------------------------------------------------------------
 public void OnPointerUp(PointerEventData eventData)
 {
  //Debug.Log("OnPointerUp()");
 }

 // ----------------------------------------------------------------------------
 void RestoreCachedVertexAttributes(int index)
 {
  if (index == -1 || index > m_TextMeshPro.textInfo.characterCount - 1) return;

  // Get the index of the material / sub text object used by this character.
  int materialIndex = m_TextMeshPro.textInfo.characterInfo[index].materialReferenceIndex;

  // Get the index of the first vertex of the selected character.
  int vertexIndex = m_TextMeshPro.textInfo.characterInfo[index].vertexIndex;

  // Restore Vertices
  // Get a reference to the cached / original vertices.
  Vector3[] src_vertices = m_cachedMeshInfoVertexData[materialIndex].vertices;

  // Get a reference to the vertices that we need to replace.
  Vector3[] dst_vertices = m_TextMeshPro.textInfo.meshInfo[materialIndex].vertices;

  // Restore / Copy vertices from source to destination
  dst_vertices[vertexIndex + 0] = src_vertices[vertexIndex + 0];
  dst_vertices[vertexIndex + 1] = src_vertices[vertexIndex + 1];
  dst_vertices[vertexIndex + 2] = src_vertices[vertexIndex + 2];
  dst_vertices[vertexIndex + 3] = src_vertices[vertexIndex + 3];

  // Restore Vertex Colors
  // Get a reference to the vertex colors we need to replace.
  Color32[] dst_colors = m_TextMeshPro.textInfo.meshInfo[materialIndex].colors32;

  // Get a reference to the cached / original vertex colors.
  Color32[] src_colors = m_cachedMeshInfoVertexData[materialIndex].colors32;

  // Copy the vertex colors from source to destination.
  dst_colors[vertexIndex + 0] = src_colors[vertexIndex + 0];
  dst_colors[vertexIndex + 1] = src_colors[vertexIndex + 1];
  dst_colors[vertexIndex + 2] = src_colors[vertexIndex + 2];
  dst_colors[vertexIndex + 3] = src_colors[vertexIndex + 3];

  // Restore UV0S
  // UVS0
  Vector2[] src_uv0s = m_cachedMeshInfoVertexData[materialIndex].uvs0;
  Vector2[] dst_uv0s = m_TextMeshPro.textInfo.meshInfo[materialIndex].uvs0;
  dst_uv0s[vertexIndex + 0] = src_uv0s[vertexIndex + 0];
  dst_uv0s[vertexIndex + 1] = src_uv0s[vertexIndex + 1];
  dst_uv0s[vertexIndex + 2] = src_uv0s[vertexIndex + 2];
  dst_uv0s[vertexIndex + 3] = src_uv0s[vertexIndex + 3];

  // UVS2
  Vector2[] src_uv2s = m_cachedMeshInfoVertexData[materialIndex].uvs2;
  Vector2[] dst_uv2s = m_TextMeshPro.textInfo.meshInfo[materialIndex].uvs2;
  dst_uv2s[vertexIndex + 0] = src_uv2s[vertexIndex + 0];
  dst_uv2s[vertexIndex + 1] = src_uv2s[vertexIndex + 1];
  dst_uv2s[vertexIndex + 2] = src_uv2s[vertexIndex + 2];
  dst_uv2s[vertexIndex + 3] = src_uv2s[vertexIndex + 3];


  // Restore last vertex attribute as we swapped it as well
  int lastIndex = (src_vertices.Length / 4 - 1) * 4;

  // Vertices
  dst_vertices[lastIndex + 0] = src_vertices[lastIndex + 0];
  dst_vertices[lastIndex + 1] = src_vertices[lastIndex + 1];
  dst_vertices[lastIndex + 2] = src_vertices[lastIndex + 2];
  dst_vertices[lastIndex + 3] = src_vertices[lastIndex + 3];

  // Vertex Colors
  src_colors = m_cachedMeshInfoVertexData[materialIndex].colors32;
  dst_colors = m_TextMeshPro.textInfo.meshInfo[materialIndex].colors32;
  dst_colors[lastIndex + 0] = src_colors[lastIndex + 0];
  dst_colors[lastIndex + 1] = src_colors[lastIndex + 1];
  dst_colors[lastIndex + 2] = src_colors[lastIndex + 2];
  dst_colors[lastIndex + 3] = src_colors[lastIndex + 3];

  // UVS0
  src_uv0s = m_cachedMeshInfoVertexData[materialIndex].uvs0;
  dst_uv0s = m_TextMeshPro.textInfo.meshInfo[materialIndex].uvs0;
  dst_uv0s[lastIndex + 0] = src_uv0s[lastIndex + 0];
  dst_uv0s[lastIndex + 1] = src_uv0s[lastIndex + 1];
  dst_uv0s[lastIndex + 2] = src_uv0s[lastIndex + 2];
  dst_uv0s[lastIndex + 3] = src_uv0s[lastIndex + 3];

  // UVS2
  src_uv2s = m_cachedMeshInfoVertexData[materialIndex].uvs2;
  dst_uv2s = m_TextMeshPro.textInfo.meshInfo[materialIndex].uvs2;
  dst_uv2s[lastIndex + 0] = src_uv2s[lastIndex + 0];
  dst_uv2s[lastIndex + 1] = src_uv2s[lastIndex + 1];
  dst_uv2s[lastIndex + 2] = src_uv2s[lastIndex + 2];
  dst_uv2s[lastIndex + 3] = src_uv2s[lastIndex + 3];

  // Need to update the appropriate 
  m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
 }
}

Tuesday, May 15, 2018

CommandInvokationFailure: While Compiling for Android

While compiling for Android I got the following error:

CommandInvokationFailure: Failed to build apk. See the Console for details.
C:\Program Files\Java\jdk-10.0.1\bin\java.exe -Xmx2048M -Dcom.android.sdkmanager.toolsdir="C:/Program Files (x86)/Android/android-sdk\tools" -Dfile.encoding=UTF8 -jar "C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\sdktools.jar"


Solution:

Use Java JDK 8. 

It seems that Unity 5.3.8 doesn't like JDK 9 or higher.
Downloaded 
jdk-8u171-windows-x64 from Oracle and install it.

Then in Unity under Edit > Preferences > External Tools point Unity to JDK 8.


Android SDK is outdated

I finished making the make screens for the game and it runs well when compiled for PC. Ultimately I want the game to run on Android and iOS so I thought I'd compile an APK file and try and get it running on my Samsung S6.

After following the instructions I found here: Building your Unity game to an Android device for testing

I installed Java JDK and Android Studio and pointed Unity to the tools directory that contains the Android SDK.


The Problem


However when I compiled for Android I kept getting the following messages:

"Android SDK is outdated" and "sdk tools version 23 < 24".

Unity then asked me if I wanted to update and after replying "yes" it went off, looped around a bit and then came back with same messages.



The Solution


All that was needed was for Unity to be running in Administrator mode.
Once in Administrator mode Unity managed to download what it needed and continue compiling.

So I decided that I want to make a game in Unity 3D


A Brief Prologue

I've been thinking about this game for about a year now and I've dabbled with Unity 3D on and off over a couple of years. A few years ago I started a Udemy course in Unity which I found pretty good. I only completed the 2D section which I think is all I need to make this game. Link: Complete C# Unity Developer 2D - Learn to Code Making Games

I have a background in programming and graphic design and I've worked a few years in the gaming industry so I'm hoping I know what I'm getting myself and I'm not going to bite off more than I can chew, which is often the case with wanna be game developers.

Anyway I'm not going to go into any detail about what this game is about as that is not the purpose of the blog.

The Purpose of this Blog

The purpose of this blog or rather Dev Diary is to catalogue the problems and solutions I encounter while making this game. This is for both myself as much as for anyone reading this - as I know from experience that I will solve many issues and forget how I solved them when I come across them again.

Also

This is the first time I've used Blogger so things may look a little rough for awhile.