public Engine(EngineSettings engineSettings)
	{
		this.engineSettings = engineSettings;
		entities = new ConcurrentHashMap<Integer, EngineEntity>();
		AIs = new ArrayList<>();
		playerSprites = SpriteSheet.getPlayerSprites();
		gunSprites = SpriteSheet.getWeaponSprite();
		player = new SpriteCharacter(engineSettings.getWidth() / 2.0, (double)engineSettings.getHeight() - playerSprites.get(0).getHeight(), engineSettings.getWidth(), new SpriteAnimation(playerSprites, gunSprites.get(0), 0.2));
	
		/*
		for(int i = 1; i < 5; ++i)
		{
			AI tmp = new AI(i * 90.0, (double)engineSettings.getHeight() - playerSprites.get(0).getHeight(), engineSettings.getWidth(),
					new SpriteAnimation(playerSprites, gunSprites.get(0), 0.2), player);
			tmp.setMomentum(0.2);
			AIs.add(tmp);
			entities.put(tmp.hashCode(), tmp);
		}
		*/
		
		entities.put(player.hashCode(), player);
		entities.putAll(makeBalls(engineSettings).stream().collect(Collectors.toConcurrentMap(Ball::hashCode, b -> b)));
//				.ballCount(4)
//				.speed(2)
//				.ballSize(20)
//				.width(16*50)
//				.height(9*50)
//				.momentum(0.5)
//				.build())
//				.stream().collect(Collectors.toConcurrentMap(Ball::hashCode, b -> b)));
		
		this.addMouseListener(new ProjectileListener(player, entities));
		this.addMouseMotionListener(new CrosshairListener(player));
		this.addKeyListener(new ControllerListener(player));
		
		// JPanel to draw in.
		setSize(new Dimension(engineSettings.getWidth(), engineSettings.getHeight()));
		setPreferredSize(new Dimension(engineSettings.getWidth(), engineSettings.getHeight()));
		setMinimumSize(getPreferredSize());
		setMaximumSize(getPreferredSize());
		setFocusable(true);
		requestFocus();
		setVisible(true);
		
		// Create a new Buffered Image to draw in.
		// swing or awt already support double buffering, this is to use bufferedimage getRGB method.
		image = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_BGR);
	}