Sample 1: A shader in Unity (using HLSL), to create a repeating, translucent holographic pattern shown on objects in the world. Written for my project Tunnels.
Shader "Unlit/Hologram"
{
Properties
{
_Color("Color", Color) = (1, 0, 0, 1)
_Brightness("Brightness", Range(0, 1)) = 0.16
_Contrast("Contrast", Range(0, 10)) = 1
_Proximity("Band Proximity", Range(1, 50)) = 10 //Related to size of the grid in world space: larger means bands are closer together
}
SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent Cutout" }
LOD 100
Blend SrcAlpha One
Cull Off
ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 objVertex : TEXCOORD2;
};
fixed4 _Color;
float _Brightness;
float _Contrast;
float _Proximity;
//Gets each vertex in the object to color
v2f vert (appdata v)
{
v2f o;
o.objVertex = mul(unity_ObjectToWorld, v.vertex); //Needed to tie shader to the world, rather than object, coordinates
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
//Applies a formula to a specific coordinate for use in the frag function
float modCoordVal(float c, float mult) { return max(0.35, cos(c * _Proximity * mult)); }
float modCoordVal(float c, float mult, float exp) { return max(0.35, pow(cos(c * _Proximity * mult), exp)); }
//This function creates the repeating pattern using cosine and powers to color the object.
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = _Color * (modCoordVal(i.objVertex.y, 0.2) + modCoordVal(i.objVertex.z, 0.1, 5) + modCoordVal(i.objVertex.x, 0.1, 5));
col += _Color * (modCoordVal(i.objVertex.y, 1) + modCoordVal(i.objVertex.z, 0.5, 5) + modCoordVal(i.objVertex.x, 0.5, 5));
col = pow(col * _Brightness, _Contrast);
return col;
}
ENDCG
}
}
}
Sample 2: A first person camera control script written in C# for Unity. Used in my project Dim.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ControlCamera : MonoBehaviour
{
float lookSpeed = 0.5f;
int smoothAmount = 8;
Vector2[] lookVectors;
private Vector2 averageLookVector = Vector2.zero;
//Start is called before the first frame update
void Start()
{
lookVectors = new Vector2[smoothAmount];
for (int i = 0; i < smoothAmount; i++) lookVectors[i] = Vector2.zero;
}
//Update is called once per frame
void Update()
{
averageLookVector = Vector2.zero;
Vector2 currLookVec = new Vector2(Input.GetAxis("Mouse X") * lookSpeed, Input.GetAxis("Mouse Y") * -lookSpeed);
//Smooths camera rotation by averaging the raw mouse delta out over a number of frames
for (int i = 0; i < lookVectors.Length-1; i++)
{
lookVectors[i] = lookVectors[i + 1];
averageLookVector += lookVectors[i];
}
lookVectors[lookVectors.Length-1] = currLookVec;
averageLookVector += currLookVec;
averageLookVector /= smoothAmount;
transform.parent.Rotate(0f, averageLookVector.x, 0f); //Rotate the camera's parent (the player game object) on the horizontal axis to fit with WASD controls
transform.Rotate(averageLookVector.y, 0f, 0f);
transform.localRotation = Quaternion.Euler(ClampAngle(transform.rotation.eulerAngles.x, -80f, 80f), 0f, 0f); //Clamp vertical look rotation
}
/// <summary> Clamps an angle between a minimum and maximum value. </summary>
float ClampAngle(float angle, float min, float max)
{
if (angle < 0f) angle += 360f;
if (angle < 180f) return Mathf.Min(angle, min + 360f);
else return Mathf.Max(angle, max);
}
}
Sample 3: The main loop of my tower defense o, written in Java/Processing.
import processing.sound.*;
//This file contains the main functions, variables, and game loop
color[][] schemes;
Enemy[][] gameWaves;
//declare enums for color scheme, game screen, etc.
enum ColorScheme
{
NEON (0), DARK (1), LIGHT (2), EYESORE (3);
private int index;
private ColorScheme(int index) { this.index = index; }
}
enum Screen { MENU, GAME, TUTORIAL, VICTORY, GAMEOVER, PAUSE }
enum Building { WALL, TURRET, MORTAR, CALTROP };
enum Music { MENUTRACK, GAMETRACK };
enum GameState { NOTENDED, LOSS, VICTORY };
ColorScheme scheme = ColorScheme.NEON;
Screen screen = Screen.MENU;
Building buildIndex = Building.WALL;
Music musIndex = Music.MENUTRACK;
GameState state = GameState.NOTENDED;
//Initialize lists of game objects
ArrayList<Wall> walls = new ArrayList<Wall>();
ArrayList<Turret> turrets = new ArrayList<Turret>();
ArrayList<Mortar> mortars = new ArrayList<Mortar>();
ArrayList<Caltrop> caltrops = new ArrayList<Caltrop>();
ArrayList<Enemy> enemies = new ArrayList<Enemy>();
ArrayList<TurretProjectile> tShots = new ArrayList<TurretProjectile>();
ArrayList<MortarProjectile> mShots = new ArrayList<MortarProjectile>();
//Declare game screen objects
OLetter oLetter;
MenuScreen menuScreen;
TutorialScreen tutorialScreen;
GameScreen gameScreen;
VictoryScreen victoryScreen;
GameOverScreen gameOverScreen;
PauseScreen pauseScreen;
int wave = 0; //declare and init wave number
int orium = 100; //declare and init amount of orium
float gameAnimSize = 0; //the starting size of the game end animation
SoundFile menuMusic; //declare menu and game music
SoundFile gameMusic;
Sound soundMaster;
float volume = 0.4;
float targetVolume = 0.4;
PImage icon;
//Setup is called when the game starts
void setup()
{
size(960, 720);
icon = loadImage("o_icon.png");
refreshScheme(); //initializes color schemes
refreshWaves(); //inits the schedule of enemies
oLetter = new OLetter(); //inits o and the screen objects
menuScreen = new MenuScreen();
tutorialScreen = new TutorialScreen();
gameScreen = new GameScreen();
victoryScreen = new VictoryScreen();
gameOverScreen = new GameOverScreen();
pauseScreen = new PauseScreen();
menuMusic = new SoundFile(this, "ombience.wav");
gameMusic = new SoundFile(this, "sonorance.wav");
soundMaster = new Sound(this);
soundMaster.volume(volume);
}
//Draw is called every frame
void draw()
{
background(schemes[scheme.index][0]); //color background according to the scheme
oLetter.update(); //update the o object
//Adjust volume if necessary
if (volume < targetVolume)
{
volume += 0.05;
if (volume > targetVolume) volume = targetVolume;
}
else if (volume > targetVolume)
{
volume -= 0.05;
if (volume < targetVolume) volume = targetVolume;
}
soundMaster.volume(volume);
//update UI and music according to the screen
switch(screen)
{
case MENU: menuScreen.update();
musIndex = Music.MENUTRACK;
break;
case TUTORIAL: tutorialScreen.update();
musIndex = Music.MENUTRACK;
break;
case VICTORY: victoryScreen.update();
musIndex = Music.GAMETRACK;
break;
case GAMEOVER: gameOverScreen.update();
musIndex = Music.GAMETRACK;
break;
case PAUSE: pauseScreen.update();
musIndex = Music.GAMETRACK;
break;
case GAME:
//Here all objects in play in the main game screen are displayed
for (Wall w : walls) w.display();
for (Turret t : turrets) t.update();
for (Mortar m : mortars) m.update();
for (Caltrop c : caltrops) c.display();
for (Enemy e : enemies) e.update();
for (TurretProjectile tp : tShots) tp.update();
for (MortarProjectile mp : mShots) mp.update();
gameScreen.update(); //update the UI
//These are actions dependent on whether the game is won, lost, or still going
switch(state)
{
case NOTENDED:
//go through objects that need to be deleted and remove them
for (int i = 0; i < enemies.size(); i++) if (enemies.get(i).hp <= 0) enemies.remove(i);
for (int i = 0; i < tShots.size(); i++) if (tShots.get(i).doDestroy) tShots.remove(i);
for (int i = 0; i < mShots.size(); i++) if (mShots.get(i).stage == 2) mShots.remove(i);
if (enemies.size() == 0) //if the wave is over
{
if (wave + 1 < gameWaves.length) //if it isn't the end of the game
{
wave++;
for (Enemy e : gameWaves[wave]) //set and add all the enemies in the next wave to the list
{
e.startTime = millis();
enemies.add(e);
}
refreshScheme(); //randomize the colors again
}
else state = GameState.VICTORY; //if it is, set the end condition
}
break;
//On a loss or victory, a short animation plays before transferring to the appropriate screen
case LOSS:
gameAnimSize += 20;
noStroke();
fill(schemes[scheme.index][3]);
circle(width/2, height/2, gameAnimSize);
if (gameAnimSize > 1450) screen = Screen.GAMEOVER;
break;
case VICTORY:
gameAnimSize += 20;
strokeWeight(10);
fill(schemes[scheme.index][0]);
stroke(schemes[scheme.index][2]);
circle(width/2, height/2, gameAnimSize);
if (gameAnimSize > 1450) screen = Screen.VICTORY;
break;
}
musIndex = Music.GAMETRACK;
break;
}
//Switch out currently playing music if necessary
if (musIndex == Music.MENUTRACK)
{
if (gameMusic.isPlaying()) gameMusic.stop();
if (!menuMusic.isPlaying()) menuMusic.play();
}
else
{
if (menuMusic.isPlaying()) menuMusic.stop();
if (!gameMusic.isPlaying()) gameMusic.play();
}
}
//Called when any key is pressed
void keyPressed()
{
//In the game and tutorial screens, 1-4 are hotkeys to select buildings.
if (screen == Screen.GAME || screen == Screen.TUTORIAL)
{
switch(key)
{
case '1': buildIndex = Building.WALL;
break;
case '2': buildIndex = Building.TURRET;
break;
case '3': buildIndex = Building.MORTAR;
break;
case '4': buildIndex = Building.CALTROP;
break;
}
}
//In the menu screen, those same keys may be used to set a color scheme
else if (screen == Screen.MENU)
{
switch(key)
{
case '1': scheme = ColorScheme.NEON;
break;
case '2': scheme = ColorScheme.DARK;
break;
case '3': scheme = ColorScheme.LIGHT;
break;
case '4': scheme = ColorScheme.EYESORE;
break;
}
}
//The Escape key may either bring up the pause menu or return to the main menu, depending on the screen.
if (keyCode == 27)
{
key = 0;
switch(screen)
{
case GAME: screen = Screen.PAUSE;
pauseScreen.pauseTime = millis();
targetVolume = 0.1;
break;
case TUTORIAL: screen = Screen.MENU;
break;
case VICTORY: screen = Screen.MENU;
break;
case GAMEOVER: screen = Screen.MENU;
break;
case PAUSE: screen = Screen.GAME;
for (Enemy e : enemies) e.startTime += pauseScreen.pauseDuration;
targetVolume = 0.4;
break;
default: break;
}
}
}
//Called when the mouse is released
void mouseReleased()
{
//Left clicking will either press buttons, or in the game screen place buildings.
if (mouseButton == LEFT)
{
switch(screen)
{
case MENU:
if (menuScreen.playButton.mouseOver())
{
screen = Screen.GAME;
gameScreen.init();
}
else if (menuScreen.tutorialButton.mouseOver())
{
screen = Screen.TUTORIAL;
tutorialScreen.init();
}
else if (menuScreen.neonSchemeButton.mouseOver()) scheme = ColorScheme.NEON;
else if (menuScreen.darkSchemeButton.mouseOver()) scheme = ColorScheme.DARK;
else if (menuScreen.lightSchemeButton.mouseOver()) scheme = ColorScheme.LIGHT;
else if (menuScreen.eyesoreSchemeButton.mouseOver()) scheme = ColorScheme.EYESORE;
break;
case GAME:
//This is where buildings are placed
//Aligns click position to a grid, and checks to see whether you can build there first
float placeX = floor(mouseX / 40) * 40;
float placeY = floor(mouseY / 40) * 40;
if (!buildingCollision(placeX, placeY) && calculateOrium() >= 0)
{
switch (buildIndex)
{
case WALL: Wall w = new Wall(placeX, placeY);
walls.add(w);
orium -= 10;
break;
case TURRET: Turret t = new Turret(placeX, placeY);
turrets.add(t);
orium -= 25;
break;
case MORTAR: Mortar m = new Mortar(placeX, placeY);
mortars.add(m);
orium -= 35;
break;
case CALTROP: Caltrop c = new Caltrop(placeX, placeY);
caltrops.add(c);
orium -= 15;
break;
}
}
else //These are the buttons on the game screen
{
if (gameScreen.wallButton.mouseOver()) buildIndex = Building.WALL;
else if (gameScreen.turretButton.mouseOver()) buildIndex = Building.TURRET;
else if (gameScreen.mortarButton.mouseOver()) buildIndex = Building.MORTAR;
else if (gameScreen.caltropButton.mouseOver()) buildIndex = Building.CALTROP;
}
break;
case TUTORIAL:
if (tutorialScreen.returnButton.mouseOver()) screen = Screen.MENU;
else if (tutorialScreen.wallButton.mouseOver()) buildIndex = Building.WALL;
else if (tutorialScreen.turretButton.mouseOver()) buildIndex = Building.TURRET;
else if (tutorialScreen.mortarButton.mouseOver()) buildIndex = Building.MORTAR;
else if (tutorialScreen.caltropButton.mouseOver()) buildIndex = Building.CALTROP;
break;
case VICTORY:
if (victoryScreen.returnButton.mouseOver()) screen = Screen.MENU;
break;
case GAMEOVER:
if (gameOverScreen.returnButton.mouseOver()) screen = Screen.MENU;
break;
case PAUSE:
if (pauseScreen.unpauseButton.mouseOver())
{
for (Enemy e : enemies) e.startTime += pauseScreen.pauseDuration;
screen = Screen.GAME;
targetVolume = 0.4;
}
else if (pauseScreen.menuButton.mouseOver())
{
targetVolume = 0.4;
screen = Screen.MENU;
}
break;
}
}
//Delete any placed buildings on right click
else if (mouseButton == RIGHT && screen == Screen.GAME && mouseY > 80) deleteBuilding();
}